1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 /*****************************************************************************
25  * name:		be_ai_move.c
26  *
27  * desc:		bot movement AI
28  *
29  * $Archive: /MissionPack/code/botlib/be_ai_move.c $
30  * $Author: Ttimo $
31  * $Revision: 14 $
32  * $Modtime: 4/22/01 8:52a $
33  * $Date: 4/22/01 8:52a $
34  *
35  *****************************************************************************/
36 
37 #include "qcommon/q_shared.h"
38 #include "l_memory.h"
39 #include "l_libvar.h"
40 #include "l_utils.h"
41 #include "l_script.h"
42 #include "l_precomp.h"
43 #include "l_struct.h"
44 #include "aasfile.h"
45 #include "botlib.h"
46 #include "be_aas.h"
47 #include "be_aas_funcs.h"
48 #include "be_interface.h"
49 
50 #include "be_ea.h"
51 #include "be_ai_goal.h"
52 #include "be_ai_move.h"
53 
54 
55 //#define DEBUG_AI_MOVE
56 //#define DEBUG_ELEVATOR
57 //#define DEBUG_GRAPPLE
58 
59 // bk001204 - redundant bot_avoidspot_t, see ../game/be_ai_move.h
60 
61 //movement state
62 //NOTE: the moveflags MFL_ONGROUND, MFL_TELEPORTED, MFL_WATERJUMP and
63 //		MFL_GRAPPLEPULL must be set outside the movement code
64 typedef struct bot_movestate_s
65 {
66 	//input vars (all set outside the movement code)
67 	vec3_t origin;								//origin of the bot
68 	vec3_t velocity;							//velocity of the bot
69 	vec3_t viewoffset;							//view offset
70 	int entitynum;								//entity number of the bot
71 	int client;									//client number of the bot
72 	float thinktime;							//time the bot thinks
73 	int presencetype;							//presencetype of the bot
74 	vec3_t viewangles;							//view angles of the bot
75 	//state vars
76 	int areanum;								//area the bot is in
77 	int lastareanum;							//last area the bot was in
78 	int lastgoalareanum;						//last goal area number
79 	int lastreachnum;							//last reachability number
80 	vec3_t lastorigin;							//origin previous cycle
81 	int reachareanum;							//area number of the reachabilty
82 	int moveflags;								//movement flags
83 	int jumpreach;								//set when jumped
84 	float grapplevisible_time;					//last time the grapple was visible
85 	float lastgrappledist;						//last distance to the grapple end
86 	float reachability_time;					//time to use current reachability
87 	int avoidreach[MAX_AVOIDREACH];				//reachabilities to avoid
88 	float avoidreachtimes[MAX_AVOIDREACH];		//times to avoid the reachabilities
89 	int avoidreachtries[MAX_AVOIDREACH];		//number of tries before avoiding
90 	//
91 	bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS];	//spots to avoid
92 	int numavoidspots;
93 } bot_movestate_t;
94 
95 //used to avoid reachability links for some time after being used
96 #define AVOIDREACH
97 #define AVOIDREACH_TIME			6		//avoid links for 6 seconds after use
98 #define AVOIDREACH_TRIES		4
99 //prediction times
100 #define PREDICTIONTIME_JUMP	3		//in seconds
101 #define PREDICTIONTIME_MOVE	2		//in seconds
102 //weapon indexes for weapon jumping
103 #define WEAPONINDEX_ROCKET_LAUNCHER		5
104 #define WEAPONINDEX_BFG					9
105 
106 #define MODELTYPE_FUNC_PLAT		1
107 #define MODELTYPE_FUNC_BOB		2
108 #define MODELTYPE_FUNC_DOOR		3
109 #define MODELTYPE_FUNC_STATIC	4
110 
111 libvar_t *sv_maxstep;
112 libvar_t *sv_maxbarrier;
113 libvar_t *sv_gravity;
114 libvar_t *weapindex_rocketlauncher;
115 libvar_t *weapindex_bfg10k;
116 libvar_t *weapindex_grapple;
117 libvar_t *entitytypemissile;
118 libvar_t *offhandgrapple;
119 libvar_t *cmd_grappleoff;
120 libvar_t *cmd_grappleon;
121 //type of model, func_plat or func_bobbing
122 int modeltypes[MAX_MODELS];
123 
124 bot_movestate_t *botmovestates[MAX_CLIENTS+1];
125 
126 //========================================================================
127 //
128 // Parameter:			-
129 // Returns:				-
130 // Changes Globals:		-
131 //========================================================================
BotAllocMoveState(void)132 int BotAllocMoveState(void)
133 {
134 	int i;
135 
136 	for (i = 1; i <= MAX_CLIENTS; i++)
137 	{
138 		if (!botmovestates[i])
139 		{
140 			botmovestates[i] = (struct bot_movestate_s *)GetClearedMemory(sizeof(bot_movestate_t));
141 			return i;
142 		} //end if
143 	} //end for
144 	return 0;
145 } //end of the function BotAllocMoveState
146 //========================================================================
147 //
148 // Parameter:			-
149 // Returns:				-
150 // Changes Globals:		-
151 //========================================================================
BotFreeMoveState(int handle)152 void BotFreeMoveState(int handle)
153 {
154 	if (handle <= 0 || handle > MAX_CLIENTS)
155 	{
156 		botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
157 		return;
158 	} //end if
159 	if (!botmovestates[handle])
160 	{
161 		botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
162 		return;
163 	} //end if
164 	FreeMemory(botmovestates[handle]);
165 	botmovestates[handle] = NULL;
166 } //end of the function BotFreeMoveState
167 //========================================================================
168 //
169 // Parameter:				-
170 // Returns:					-
171 // Changes Globals:		-
172 //========================================================================
BotMoveStateFromHandle(int handle)173 bot_movestate_t *BotMoveStateFromHandle(int handle)
174 {
175 	if (handle <= 0 || handle > MAX_CLIENTS)
176 	{
177 		botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
178 		return NULL;
179 	} //end if
180 	if (!botmovestates[handle])
181 	{
182 		botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
183 		return NULL;
184 	} //end if
185 	return botmovestates[handle];
186 } //end of the function BotMoveStateFromHandle
187 //========================================================================
188 //
189 // Parameter:			-
190 // Returns:				-
191 // Changes Globals:		-
192 //========================================================================
BotInitMoveState(int handle,bot_initmove_t * initmove)193 void BotInitMoveState(int handle, bot_initmove_t *initmove)
194 {
195 	bot_movestate_t *ms;
196 
197 	ms = BotMoveStateFromHandle(handle);
198 	if (!ms) return;
199 	VectorCopy(initmove->origin, ms->origin);
200 	VectorCopy(initmove->velocity, ms->velocity);
201 	VectorCopy(initmove->viewoffset, ms->viewoffset);
202 	ms->entitynum = initmove->entitynum;
203 	ms->client = initmove->client;
204 	ms->thinktime = initmove->thinktime;
205 	ms->presencetype = initmove->presencetype;
206 	VectorCopy(initmove->viewangles, ms->viewangles);
207 	//
208 	ms->moveflags &= ~MFL_ONGROUND;
209 	if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND;
210 	ms->moveflags &= ~MFL_TELEPORTED;
211 	if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED;
212 	ms->moveflags &= ~MFL_WATERJUMP;
213 	if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP;
214 	ms->moveflags &= ~MFL_WALK;
215 	if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK;
216 	ms->moveflags &= ~MFL_GRAPPLEPULL;
217 	if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL;
218 } //end of the function BotInitMoveState
219 //========================================================================
220 //
221 // Parameter:			-
222 // Returns:				-
223 // Changes Globals:		-
224 //========================================================================
AngleDiff(float ang1,float ang2)225 float AngleDiff(float ang1, float ang2)
226 {
227 	float diff;
228 
229 	diff = ang1 - ang2;
230 	if (ang1 > ang2)
231 	{
232 		if (diff > 180.0) diff -= 360.0;
233 	} //end if
234 	else
235 	{
236 		if (diff < -180.0) diff += 360.0;
237 	} //end else
238 	return diff;
239 } //end of the function AngleDiff
240 //===========================================================================
241 //
242 // Parameter:			-
243 // Returns:				-
244 // Changes Globals:		-
245 //===========================================================================
BotFuzzyPointReachabilityArea(vec3_t origin)246 int BotFuzzyPointReachabilityArea(vec3_t origin)
247 {
248 	int firstareanum, j, x, y, z;
249 	int areas[10], numareas, areanum, bestareanum;
250 	float dist, bestdist;
251 	vec3_t points[10], v, end;
252 
253 	firstareanum = 0;
254 	areanum = AAS_PointAreaNum(origin);
255 	if (areanum)
256 	{
257 		firstareanum = areanum;
258 		if (AAS_AreaReachability(areanum)) return areanum;
259 	} //end if
260 	VectorCopy(origin, end);
261 	end[2] += 4;
262 	numareas = AAS_TraceAreas(origin, end, areas, points, 10);
263 	for (j = 0; j < numareas; j++)
264 	{
265 		if (AAS_AreaReachability(areas[j])) return areas[j];
266 	} //end for
267 	bestdist = 999999;
268 	bestareanum = 0;
269 	for (z = 1; z >= -1; z -= 1)
270 	{
271 		for (x = 1; x >= -1; x -= 1)
272 		{
273 			for (y = 1; y >= -1; y -= 1)
274 			{
275 				VectorCopy(origin, end);
276 				end[0] += x * 8;
277 				end[1] += y * 8;
278 				end[2] += z * 12;
279 				numareas = AAS_TraceAreas(origin, end, areas, points, 10);
280 				for (j = 0; j < numareas; j++)
281 				{
282 					if (AAS_AreaReachability(areas[j]))
283 					{
284 						VectorSubtract(points[j], origin, v);
285 						dist = VectorLength(v);
286 						if (dist < bestdist)
287 						{
288 							bestareanum = areas[j];
289 							bestdist = dist;
290 						} //end if
291 					} //end if
292 					if (!firstareanum) firstareanum = areas[j];
293 				} //end for
294 			} //end for
295 		} //end for
296 		if (bestareanum) return bestareanum;
297 	} //end for
298 	return firstareanum;
299 } //end of the function BotFuzzyPointReachabilityArea
300 //===========================================================================
301 //
302 // Parameter:			-
303 // Returns:				-
304 // Changes Globals:		-
305 //===========================================================================
BotReachabilityArea(vec3_t origin,int client)306 int BotReachabilityArea(vec3_t origin, int client)
307 {
308 	int modelnum, modeltype, reachnum, areanum;
309 	aas_reachability_t reach;
310 	vec3_t org, end, mins, maxs, up = {0, 0, 1};
311 	bsp_trace_t bsptrace;
312 	aas_trace_t trace;
313 
314 	//check if the bot is standing on something
315 	AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
316 	VectorMA(origin, -3, up, end);
317 	bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
318 	if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE)
319 	{
320 		//if standing on the world the bot should be in a valid area
321 		if (bsptrace.ent == ENTITYNUM_WORLD)
322 		{
323 			return BotFuzzyPointReachabilityArea(origin);
324 		} //end if
325 
326 		modelnum = AAS_EntityModelindex(bsptrace.ent);
327 		modeltype = modeltypes[modelnum];
328 
329 		//if standing on a func_plat or func_bobbing then the bot is assumed to be
330 		//in the area the reachability points to
331 		if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB)
332 		{
333 			reachnum = AAS_NextModelReachability(0, modelnum);
334 			if (reachnum)
335 			{
336 				AAS_ReachabilityFromNum(reachnum, &reach);
337 				return reach.areanum;
338 			} //end if
339 		} //end else if
340 
341 		//if the bot is swimming the bot should be in a valid area
342 		if (AAS_Swimming(origin))
343 		{
344 			return BotFuzzyPointReachabilityArea(origin);
345 		} //end if
346 		//
347 		areanum = BotFuzzyPointReachabilityArea(origin);
348 		//if the bot is in an area with reachabilities
349 		if (areanum && AAS_AreaReachability(areanum)) return areanum;
350 		//trace down till the ground is hit because the bot is standing on some other entity
351 		VectorCopy(origin, org);
352 		VectorCopy(org, end);
353 		end[2] -= 800;
354 		trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1);
355 		if (!trace.startsolid)
356 		{
357 			VectorCopy(trace.endpos, org);
358 		} //end if
359 		//
360 		return BotFuzzyPointReachabilityArea(org);
361 	} //end if
362 	//
363 	return BotFuzzyPointReachabilityArea(origin);
364 } //end of the function BotReachabilityArea
365 //===========================================================================
366 // returns the reachability area the bot is in
367 //
368 // Parameter:				-
369 // Returns:					-
370 // Changes Globals:		-
371 //===========================================================================
372 /*
373 int BotReachabilityArea(vec3_t origin, int testground)
374 {
375 	int firstareanum, i, j, x, y, z;
376 	int areas[10], numareas, areanum, bestareanum;
377 	float dist, bestdist;
378 	vec3_t org, end, points[10], v;
379 	aas_trace_t trace;
380 
381 	firstareanum = 0;
382 	for (i = 0; i < 2; i++)
383 	{
384 		VectorCopy(origin, org);
385 		//if test at the ground (used when bot is standing on an entity)
386 		if (i > 0)
387 		{
388 			VectorCopy(origin, end);
389 			end[2] -= 800;
390 			trace = AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1);
391 			if (!trace.startsolid)
392 			{
393 				VectorCopy(trace.endpos, org);
394 			} //end if
395 		} //end if
396 
397 		firstareanum = 0;
398 		areanum = AAS_PointAreaNum(org);
399 		if (areanum)
400 		{
401 			firstareanum = areanum;
402 			if (AAS_AreaReachability(areanum)) return areanum;
403 		} //end if
404 		bestdist = 999999;
405 		bestareanum = 0;
406 		for (z = 1; z >= -1; z -= 1)
407 		{
408 			for (x = 1; x >= -1; x -= 1)
409 			{
410 				for (y = 1; y >= -1; y -= 1)
411 				{
412 					VectorCopy(org, end);
413 					end[0] += x * 8;
414 					end[1] += y * 8;
415 					end[2] += z * 12;
416 					numareas = AAS_TraceAreas(org, end, areas, points, 10);
417 					for (j = 0; j < numareas; j++)
418 					{
419 						if (AAS_AreaReachability(areas[j]))
420 						{
421 							VectorSubtract(points[j], org, v);
422 							dist = VectorLength(v);
423 							if (dist < bestdist)
424 							{
425 								bestareanum = areas[j];
426 								bestdist = dist;
427 							} //end if
428 						} //end if
429 					} //end for
430 				} //end for
431 			} //end for
432 			if (bestareanum) return bestareanum;
433 		} //end for
434 		if (!testground) break;
435 	} //end for
436 //#ifdef DEBUG
437 	//botimport.Print(PRT_MESSAGE, "no reachability area\n");
438 //#endif //DEBUG
439 	return firstareanum;
440 } //end of the function BotReachabilityArea*/
441 //===========================================================================
442 //
443 // Parameter:			-
444 // Returns:				-
445 // Changes Globals:		-
446 //===========================================================================
BotOnMover(vec3_t origin,int entnum,aas_reachability_t * reach)447 int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach)
448 {
449 	int i, modelnum;
450 	vec3_t mins, maxs, modelorigin, org, end;
451 	vec3_t angles = {0, 0, 0};
452 	vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8};
453 	bsp_trace_t trace;
454 
455 	modelnum = reach->facenum & 0x0000FFFF;
456 	//get some bsp model info
457 	AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
458 	//
459 	if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin))
460 	{
461 		botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
462 		return qfalse;
463 	} //end if
464 	//
465 	for (i = 0; i < 2; i++)
466 	{
467 		if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse;
468 		if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse;
469 	} //end for
470 	//
471 	VectorCopy(origin, org);
472 	org[2] += 24;
473 	VectorCopy(origin, end);
474 	end[2] -= 48;
475 	//
476 	trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
477 	if (!trace.startsolid && !trace.allsolid)
478 	{
479 		//NOTE: the reachability face number is the model number of the elevator
480 		if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum)
481 		{
482 			return qtrue;
483 		} //end if
484 	} //end if
485 	return qfalse;
486 } //end of the function BotOnMover
487 //===========================================================================
488 //
489 // Parameter:			-
490 // Returns:				-
491 // Changes Globals:		-
492 //===========================================================================
MoverDown(aas_reachability_t * reach)493 int MoverDown(aas_reachability_t *reach)
494 {
495 	int modelnum;
496 	vec3_t mins, maxs, origin;
497 	vec3_t angles = {0, 0, 0};
498 
499 	modelnum = reach->facenum & 0x0000FFFF;
500 	//get some bsp model info
501 	AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
502 	//
503 	if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
504 	{
505 		botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
506 		return qfalse;
507 	} //end if
508 	//if the top of the plat is below the reachability start point
509 	if (origin[2] + maxs[2] < reach->start[2]) return qtrue;
510 	return qfalse;
511 } //end of the function MoverDown
512 //========================================================================
513 //
514 // Parameter:			-
515 // Returns:				-
516 // Changes Globals:		-
517 //========================================================================
BotSetBrushModelTypes(void)518 void BotSetBrushModelTypes(void)
519 {
520 	int ent, modelnum;
521 	char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
522 
523 	Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int));
524 	//
525 	for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
526 	{
527 		if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
528 		if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue;
529 		if (model[0]) modelnum = atoi(model+1);
530 		else modelnum = 0;
531 
532 		if (modelnum < 0 || modelnum >= MAX_MODELS)
533 		{
534 			botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname);
535 			continue;
536 		} //end if
537 
538 		if (!Q_stricmp(classname, "func_bobbing"))
539 			modeltypes[modelnum] = MODELTYPE_FUNC_BOB;
540 		else if (!Q_stricmp(classname, "func_plat"))
541 			modeltypes[modelnum] = MODELTYPE_FUNC_PLAT;
542 		else if (!Q_stricmp(classname, "func_door"))
543 			modeltypes[modelnum] = MODELTYPE_FUNC_DOOR;
544 		else if (!Q_stricmp(classname, "func_static"))
545 			modeltypes[modelnum] = MODELTYPE_FUNC_STATIC;
546 	} //end for
547 } //end of the function BotSetBrushModelTypes
548 //===========================================================================
549 //
550 // Parameter:			-
551 // Returns:				-
552 // Changes Globals:		-
553 //===========================================================================
BotOnTopOfEntity(bot_movestate_t * ms)554 int BotOnTopOfEntity(bot_movestate_t *ms)
555 {
556 	vec3_t mins, maxs, end, up = {0, 0, 1};
557 	bsp_trace_t trace;
558 
559 	AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
560 	VectorMA(ms->origin, -3, up, end);
561 	trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
562 	if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
563 	{
564 		return trace.ent;
565 	} //end if
566 	return -1;
567 } //end of the function BotOnTopOfEntity
568 //===========================================================================
569 //
570 // Parameter:			-
571 // Returns:				-
572 // Changes Globals:		-
573 //===========================================================================
BotValidTravel(vec3_t origin,aas_reachability_t * reach,int travelflags)574 int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags)
575 {
576 	//if the reachability uses an unwanted travel type
577 	if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse;
578 	//don't go into areas with bad travel types
579 	if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse;
580 	return qtrue;
581 } //end of the function BotValidTravel
582 //===========================================================================
583 //
584 // Parameter:				-
585 // Returns:					-
586 // Changes Globals:		-
587 //===========================================================================
BotAddToAvoidReach(bot_movestate_t * ms,int number,float avoidtime)588 void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime)
589 {
590 	int i;
591 
592 	for (i = 0; i < MAX_AVOIDREACH; i++)
593 	{
594 		if (ms->avoidreach[i] == number)
595 		{
596 			if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++;
597 			else ms->avoidreachtries[i] = 1;
598 			ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
599 			return;
600 		} //end if
601 	} //end for
602 	//add the reachability to the reachabilities to avoid for a while
603 	for (i = 0; i < MAX_AVOIDREACH; i++)
604 	{
605 		if (ms->avoidreachtimes[i] < AAS_Time())
606 		{
607 			ms->avoidreach[i] = number;
608 			ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
609 			ms->avoidreachtries[i] = 1;
610 			return;
611 		} //end if
612 	} //end for
613 } //end of the function BotAddToAvoidReach
614 //===========================================================================
615 //
616 // Parameter:			-
617 // Returns:				-
618 // Changes Globals:		-
619 //===========================================================================
DistanceFromLineSquared(vec3_t p,vec3_t lp1,vec3_t lp2)620 float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2)
621 {
622 	vec3_t proj, dir;
623 	int j;
624 
625 	AAS_ProjectPointOntoVector(p, lp1, lp2, proj);
626 	for (j = 0; j < 3; j++)
627 		if ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||
628 			(proj[j] < lp1[j] && proj[j] < lp2[j]))
629 			break;
630 	if (j < 3) {
631 		if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
632 			VectorSubtract(p, lp1, dir);
633 		else
634 			VectorSubtract(p, lp2, dir);
635 		return VectorLengthSquared(dir);
636 	}
637 	VectorSubtract(p, proj, dir);
638 	return VectorLengthSquared(dir);
639 } //end of the function DistanceFromLineSquared
640 //===========================================================================
641 //
642 // Parameter:			-
643 // Returns:				-
644 // Changes Globals:		-
645 //===========================================================================
VectorDistanceSquared(vec3_t p1,vec3_t p2)646 float VectorDistanceSquared(vec3_t p1, vec3_t p2)
647 {
648 	vec3_t dir;
649 	VectorSubtract(p2, p1, dir);
650 	return VectorLengthSquared(dir);
651 } //end of the function VectorDistanceSquared
652 //===========================================================================
653 //
654 // Parameter:			-
655 // Returns:				-
656 // Changes Globals:		-
657 //===========================================================================
BotAvoidSpots(vec3_t origin,aas_reachability_t * reach,bot_avoidspot_t * avoidspots,int numavoidspots)658 int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots)
659 {
660 	int checkbetween, i, type;
661 	float squareddist, squaredradius;
662 
663 	switch(reach->traveltype & TRAVELTYPE_MASK)
664 	{
665 		case TRAVEL_WALK: checkbetween = qtrue; break;
666 		case TRAVEL_CROUCH: checkbetween = qtrue; break;
667 		case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break;
668 		case TRAVEL_LADDER: checkbetween = qtrue; break;
669 		case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break;
670 		case TRAVEL_JUMP: checkbetween = qfalse; break;
671 		case TRAVEL_SWIM: checkbetween = qtrue; break;
672 		case TRAVEL_WATERJUMP: checkbetween = qtrue; break;
673 		case TRAVEL_TELEPORT: checkbetween = qfalse; break;
674 		case TRAVEL_ELEVATOR: checkbetween = qfalse; break;
675 		case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break;
676 		case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break;
677 		case TRAVEL_BFGJUMP: checkbetween = qfalse; break;
678 		case TRAVEL_JUMPPAD: checkbetween = qfalse; break;
679 		case TRAVEL_FUNCBOB: checkbetween = qfalse; break;
680 		default: checkbetween = qtrue; break;
681 	} //end switch
682 
683 	type = AVOID_CLEAR;
684 	for (i = 0; i < numavoidspots; i++)
685 	{
686 		squaredradius = Square(avoidspots[i].radius);
687 		squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start);
688 		// if moving towards the avoid spot
689 		if (squareddist < squaredradius &&
690 			VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist)
691 		{
692 			type = avoidspots[i].type;
693 		} //end if
694 		else if (checkbetween) {
695 			squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end);
696 			// if moving towards the avoid spot
697 			if (squareddist < squaredradius &&
698 				VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
699 			{
700 				type = avoidspots[i].type;
701 			} //end if
702 		} //end if
703 		else
704 		{
705 			VectorDistanceSquared(avoidspots[i].origin, reach->end);
706 			// if the reachability leads closer to the avoid spot
707 			if (squareddist < squaredradius &&
708 				VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
709 			{
710 				type = avoidspots[i].type;
711 			} //end if
712 		} //end else
713 		if (type == AVOID_ALWAYS)
714 			return type;
715 	} //end for
716 	return type;
717 } //end of the function BotAvoidSpots
718 //===========================================================================
719 //
720 // Parameter:			-
721 // Returns:				-
722 // Changes Globals:		-
723 //===========================================================================
BotAddAvoidSpot(int movestate,vec3_t origin,float radius,int type)724 void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type)
725 {
726 	bot_movestate_t *ms;
727 
728 	ms = BotMoveStateFromHandle(movestate);
729 	if (!ms) return;
730 	if (type == AVOID_CLEAR)
731 	{
732 		ms->numavoidspots = 0;
733 		return;
734 	} //end if
735 
736 	if (ms->numavoidspots >= MAX_AVOIDSPOTS)
737 		return;
738 	VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin);
739 	ms->avoidspots[ms->numavoidspots].radius = radius;
740 	ms->avoidspots[ms->numavoidspots].type = type;
741 	ms->numavoidspots++;
742 } //end of the function BotAddAvoidSpot
743 //===========================================================================
744 //
745 // Parameter:			-
746 // Returns:				-
747 // Changes Globals:		-
748 //===========================================================================
BotGetReachabilityToGoal(vec3_t origin,int areanum,int lastgoalareanum,int lastareanum,int * avoidreach,float * avoidreachtimes,int * avoidreachtries,bot_goal_t * goal,int travelflags,struct bot_avoidspot_s * avoidspots,int numavoidspots,int * flags)749 int BotGetReachabilityToGoal(vec3_t origin, int areanum,
750 									  int lastgoalareanum, int lastareanum,
751 									  int *avoidreach, float *avoidreachtimes, int *avoidreachtries,
752 									  bot_goal_t *goal, int travelflags,
753 									  struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags)
754 {
755 	int i, t, besttime, bestreachnum, reachnum;
756 	aas_reachability_t reach;
757 
758 	//if not in a valid area
759 	if (!areanum) return 0;
760 	//
761 	if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum))
762 	{
763 		travelflags |= TFL_DONOTENTER;
764 	} //end if
765 	//use the routing to find the next area to go to
766 	besttime = 0;
767 	bestreachnum = 0;
768 	//
769 	for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum;
770 		reachnum = AAS_NextAreaReachability(areanum, reachnum))
771 	{
772 #ifdef AVOIDREACH
773 		//check if it isn't a reachability to avoid
774 		for (i = 0; i < MAX_AVOIDREACH; i++)
775 		{
776 			if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break;
777 		} //end for
778 		if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES)
779 		{
780 #ifdef DEBUG
781 			if (botDeveloper)
782 			{
783 				botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]);
784 			} //end if
785 #endif //DEBUG
786 			continue;
787 		} //end if
788 #endif //AVOIDREACH
789 		//get the reachability from the number
790 		AAS_ReachabilityFromNum(reachnum, &reach);
791 		//NOTE: do not go back to the previous area if the goal didn't change
792 		//NOTE: is this actually avoidance of local routing minima between two areas???
793 		if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue;
794 		//if (AAS_AreaContentsTravelFlags(reach.areanum) & ~travelflags) continue;
795 		//if the travel isn't valid
796 		if (!BotValidTravel(origin, &reach, travelflags)) continue;
797 		//get the travel time
798 		t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags);
799 		//if the goal area isn't reachable from the reachable area
800 		if (!t) continue;
801 		//if the bot should not use this reachability to avoid bad spots
802 		if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) {
803 			if (flags) {
804 				*flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT;
805 			}
806 			continue;
807 		}
808 		//add the travel time towards the area
809 		t += reach.traveltime;// + AAS_AreaTravelTime(areanum, origin, reach.start);
810 		//if the travel time is better than the ones already found
811 		if (!besttime || t < besttime)
812 		{
813 			besttime = t;
814 			bestreachnum = reachnum;
815 		} //end if
816 	} //end for
817 	//
818 	return bestreachnum;
819 } //end of the function BotGetReachabilityToGoal
820 //===========================================================================
821 //
822 // Parameter:				-
823 // Returns:					-
824 // Changes Globals:		-
825 //===========================================================================
BotAddToTarget(vec3_t start,vec3_t end,float maxdist,float * dist,vec3_t target)826 int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target)
827 {
828 	vec3_t dir;
829 	float curdist;
830 
831 	VectorSubtract(end, start, dir);
832 	curdist = VectorNormalize(dir);
833 	if (*dist + curdist < maxdist)
834 	{
835 		VectorCopy(end, target);
836 		*dist += curdist;
837 		return qfalse;
838 	} //end if
839 	else
840 	{
841 		VectorMA(start, maxdist - *dist, dir, target);
842 		*dist = maxdist;
843 		return qtrue;
844 	} //end else
845 } //end of the function BotAddToTarget
846 
BotMovementViewTarget(int movestate,bot_goal_t * goal,int travelflags,float lookahead,vec3_t target)847 int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target)
848 {
849 	aas_reachability_t reach;
850 	int reachnum, lastareanum;
851 	bot_movestate_t *ms;
852 	vec3_t end;
853 	float dist;
854 
855 	ms = BotMoveStateFromHandle(movestate);
856 	if (!ms) return qfalse;
857 	//if the bot has no goal or no last reachability
858 	if (!ms->lastreachnum || !goal) return qfalse;
859 
860 	reachnum = ms->lastreachnum;
861 	VectorCopy(ms->origin, end);
862 	lastareanum = ms->lastareanum;
863 	dist = 0;
864 	while(reachnum && dist < lookahead)
865 	{
866 		AAS_ReachabilityFromNum(reachnum, &reach);
867 		if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue;
868 		//never look beyond teleporters
869 		if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue;
870 		//never look beyond the weapon jump point
871 		if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue;
872 		if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue;
873 		//don't add jump pad distances
874 		if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD &&
875 			(reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR &&
876 			(reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB)
877 		{
878 			if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue;
879 		} //end if
880 		reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum,
881 						ms->lastgoalareanum, lastareanum,
882 							ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
883 									goal, travelflags, NULL, 0, NULL);
884 		VectorCopy(reach.end, end);
885 		lastareanum = reach.areanum;
886 		if (lastareanum == goal->areanum)
887 		{
888 			BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target);
889 			return qtrue;
890 		} //end if
891 	} //end while
892 	//
893 	return qfalse;
894 } //end of the function BotMovementViewTarget
895 //===========================================================================
896 //
897 // Parameter:			-
898 // Returns:				-
899 // Changes Globals:		-
900 //===========================================================================
BotVisible(int ent,vec3_t eye,vec3_t target)901 int BotVisible(int ent, vec3_t eye, vec3_t target)
902 {
903 	bsp_trace_t trace;
904 
905 	trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
906 	if (trace.fraction >= 1) return qtrue;
907 	return qfalse;
908 } //end of the function BotVisible
909 //===========================================================================
910 //
911 // Parameter:			-
912 // Returns:				-
913 // Changes Globals:		-
914 //===========================================================================
BotPredictVisiblePosition(vec3_t origin,int areanum,bot_goal_t * goal,int travelflags,vec3_t target)915 int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target)
916 {
917 	aas_reachability_t reach;
918 	int reachnum, lastgoalareanum, lastareanum, i;
919 	int avoidreach[MAX_AVOIDREACH];
920 	float avoidreachtimes[MAX_AVOIDREACH];
921 	int avoidreachtries[MAX_AVOIDREACH];
922 	vec3_t end;
923 
924 	//if the bot has no goal or no last reachability
925 	if (!goal) return qfalse;
926 	//if the areanum is not valid
927 	if (!areanum) return qfalse;
928 	//if the goal areanum is not valid
929 	if (!goal->areanum) return qfalse;
930 
931 	Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
932 	lastgoalareanum = goal->areanum;
933 	lastareanum = areanum;
934 	VectorCopy(origin, end);
935 	//only do 20 hops
936 	for (i = 0; i < 20 && (areanum != goal->areanum); i++)
937 	{
938 		//
939 		reachnum = BotGetReachabilityToGoal(end, areanum,
940 						lastgoalareanum, lastareanum,
941 							avoidreach, avoidreachtimes, avoidreachtries,
942 									goal, travelflags, NULL, 0, NULL);
943 		if (!reachnum) return qfalse;
944 		AAS_ReachabilityFromNum(reachnum, &reach);
945 		//
946 		if (BotVisible(goal->entitynum, goal->origin, reach.start))
947 		{
948 			VectorCopy(reach.start, target);
949 			return qtrue;
950 		} //end if
951 		//
952 		if (BotVisible(goal->entitynum, goal->origin, reach.end))
953 		{
954 			VectorCopy(reach.end, target);
955 			return qtrue;
956 		} //end if
957 		//
958 		if (reach.areanum == goal->areanum)
959 		{
960 			VectorCopy(reach.end, target);
961 			return qtrue;
962 		} //end if
963 		//
964 		lastareanum = areanum;
965 		areanum = reach.areanum;
966 		VectorCopy(reach.end, end);
967 		//
968 	} //end while
969 	//
970 	return qfalse;
971 } //end of the function BotPredictVisiblePosition
972 //===========================================================================
973 //
974 // Parameter:			-
975 // Returns:				-
976 // Changes Globals:		-
977 //===========================================================================
MoverBottomCenter(aas_reachability_t * reach,vec3_t bottomcenter)978 void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter)
979 {
980 	int modelnum;
981 	vec3_t mins, maxs, origin, mids;
982 	vec3_t angles = {0, 0, 0};
983 
984 	modelnum = reach->facenum & 0x0000FFFF;
985 	//get some bsp model info
986 	AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
987 	//
988 	if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
989 	{
990 		botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
991 	} //end if
992 	//get a point just above the plat in the bottom position
993 	VectorAdd(mins, maxs, mids);
994 	VectorMA(origin, 0.5, mids, bottomcenter);
995 	bottomcenter[2] = reach->start[2];
996 } //end of the function MoverBottomCenter
997 //===========================================================================
998 //
999 // Parameter:			-
1000 // Returns:				-
1001 // Changes Globals:		-
1002 //===========================================================================
BotGapDistance(vec3_t origin,vec3_t hordir,int entnum)1003 float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum)
1004 {
1005 	int dist;
1006 	float startz;
1007 	vec3_t start, end;
1008 	aas_trace_t trace;
1009 
1010 	//do gap checking
1011 	//startz = origin[2];
1012 	//this enables walking down stairs more fluidly
1013 	{
1014 		VectorCopy(origin, start);
1015 		VectorCopy(origin, end);
1016 		end[2] -= 60;
1017 		trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
1018 		if (trace.fraction >= 1) return 1;
1019 		startz = trace.endpos[2] + 1;
1020 	}
1021 	//
1022 	for (dist = 8; dist <= 100; dist += 8)
1023 	{
1024 		VectorMA(origin, dist, hordir, start);
1025 		start[2] = startz + 24;
1026 		VectorCopy(start, end);
1027 		end[2] -= 48 + sv_maxbarrier->value;
1028 		trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
1029 		//if solid is found the bot can't walk any further and fall into a gap
1030 		if (!trace.startsolid)
1031 		{
1032 			//if it is a gap
1033 			if (trace.endpos[2] < startz - sv_maxstep->value - 8)
1034 			{
1035 				VectorCopy(trace.endpos, end);
1036 				end[2] -= 20;
1037 				if (AAS_PointContents(end) & CONTENTS_WATER) break;
1038 				//if a gap is found slow down
1039 				//botimport.Print(PRT_MESSAGE, "gap at %i\n", dist);
1040 				return dist;
1041 			} //end if
1042 			startz = trace.endpos[2];
1043 		} //end if
1044 	} //end for
1045 	return 0;
1046 } //end of the function BotGapDistance
1047 //===========================================================================
1048 //
1049 // Parameter:			-
1050 // Returns:				-
1051 // Changes Globals:		-
1052 //===========================================================================
BotCheckBarrierJump(bot_movestate_t * ms,vec3_t dir,float speed)1053 int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed)
1054 {
1055 	vec3_t start, hordir, end;
1056 	aas_trace_t trace;
1057 
1058 	VectorCopy(ms->origin, end);
1059 	end[2] += sv_maxbarrier->value;
1060 	//trace right up
1061 	trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum);
1062 	//this shouldn't happen... but we check anyway
1063 	if (trace.startsolid) return qfalse;
1064 	//if very low ceiling it isn't possible to jump up to a barrier
1065 	if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
1066 	//
1067 	hordir[0] = dir[0];
1068 	hordir[1] = dir[1];
1069 	hordir[2] = 0;
1070 	VectorNormalize(hordir);
1071 	VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end);
1072 	VectorCopy(trace.endpos, start);
1073 	end[2] = trace.endpos[2];
1074 	//trace from previous trace end pos horizontally in the move direction
1075 	trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
1076 	//again this shouldn't happen
1077 	if (trace.startsolid) return qfalse;
1078 	//
1079 	VectorCopy(trace.endpos, start);
1080 	VectorCopy(trace.endpos, end);
1081 	end[2] = ms->origin[2];
1082 	//trace down from the previous trace end pos
1083 	trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
1084 	//if solid
1085 	if (trace.startsolid) return qfalse;
1086 	//if no obstacle at all
1087 	if (trace.fraction >= 1.0) return qfalse;
1088 	//if less than the maximum step height
1089 	if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
1090 	//
1091 	EA_Jump(ms->client);
1092 	EA_Move(ms->client, hordir, speed);
1093 	ms->moveflags |= MFL_BARRIERJUMP;
1094 	//there is a barrier
1095 	return qtrue;
1096 } //end of the function BotCheckBarrierJump
1097 //===========================================================================
1098 //
1099 // Parameter:			-
1100 // Returns:				-
1101 // Changes Globals:		-
1102 //===========================================================================
BotSwimInDirection(bot_movestate_t * ms,vec3_t dir,float speed,int type)1103 int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
1104 {
1105 	vec3_t normdir;
1106 
1107 	VectorCopy(dir, normdir);
1108 	VectorNormalize(normdir);
1109 	EA_Move(ms->client, normdir, speed);
1110 	return qtrue;
1111 } //end of the function BotSwimInDirection
1112 //===========================================================================
1113 //
1114 // Parameter:			-
1115 // Returns:				-
1116 // Changes Globals:		-
1117 //===========================================================================
BotWalkInDirection(bot_movestate_t * ms,vec3_t dir,float speed,int type)1118 int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
1119 {
1120 	vec3_t hordir, cmdmove, velocity, tmpdir, origin;
1121 	int presencetype, maxframes, cmdframes, stopevent;
1122 	aas_clientmove_t move;
1123 	float dist;
1124 
1125 	if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
1126 	//if the bot is on the ground
1127 	if (ms->moveflags & MFL_ONGROUND)
1128 	{
1129 		//if there is a barrier the bot can jump on
1130 		if (BotCheckBarrierJump(ms, dir, speed)) return qtrue;
1131 		//remove barrier jump flag
1132 		ms->moveflags &= ~MFL_BARRIERJUMP;
1133 		//get the presence type for the movement
1134 		if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH;
1135 		else presencetype = PRESENCE_NORMAL;
1136 		//horizontal direction
1137 		hordir[0] = dir[0];
1138 		hordir[1] = dir[1];
1139 		hordir[2] = 0;
1140 		VectorNormalize(hordir);
1141 		//if the bot is not supposed to jump
1142 		if (!(type & MOVE_JUMP))
1143 		{
1144 			//if there is a gap, try to jump over it
1145 			if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP;
1146 		} //end if
1147 		//get command movement
1148 		VectorScale(hordir, speed, cmdmove);
1149 		VectorCopy(ms->velocity, velocity);
1150 		//
1151 		if (type & MOVE_JUMP)
1152 		{
1153 			//botimport.Print(PRT_MESSAGE, "trying jump\n");
1154 			cmdmove[2] = 400;
1155 			maxframes = PREDICTIONTIME_JUMP / 0.1;
1156 			cmdframes = 1;
1157 			stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE|
1158 						SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
1159 		} //end if
1160 		else
1161 		{
1162 			maxframes = 2;
1163 			cmdframes = 2;
1164 			stopevent = SE_HITGROUNDDAMAGE|
1165 						SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
1166 		} //end else
1167 		//AAS_ClearShownDebugLines();
1168 		//
1169 		VectorCopy(ms->origin, origin);
1170 		origin[2] += 0.5;
1171 		AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue,
1172 									velocity, cmdmove, cmdframes, maxframes, 0.1f,
1173 									stopevent, 0, qfalse);//qtrue);
1174 		//if prediction time wasn't enough to fully predict the movement
1175 		if (move.frames >= maxframes && (type & MOVE_JUMP))
1176 		{
1177 			//botimport.Print(PRT_MESSAGE, "client %d: max prediction frames\n", ms->client);
1178 			return qfalse;
1179 		} //end if
1180 		//don't enter slime or lava and don't fall from too high
1181 		if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
1182 		{
1183 			//botimport.Print(PRT_MESSAGE, "client %d: would be hurt ", ms->client);
1184 			//if (move.stopevent & SE_ENTERSLIME) botimport.Print(PRT_MESSAGE, "slime\n");
1185 			//if (move.stopevent & SE_ENTERLAVA) botimport.Print(PRT_MESSAGE, "lava\n");
1186 			//if (move.stopevent & SE_HITGROUNDDAMAGE) botimport.Print(PRT_MESSAGE, "hitground\n");
1187 			return qfalse;
1188 		} //end if
1189 		//if ground was hit
1190 		if (move.stopevent & SE_HITGROUND)
1191 		{
1192 			//check for nearby gap
1193 			VectorNormalize2(move.velocity, tmpdir);
1194 			dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum);
1195 			if (dist > 0) return qfalse;
1196 			//
1197 			dist = BotGapDistance(move.endpos, hordir, ms->entitynum);
1198 			if (dist > 0) return qfalse;
1199 		} //end if
1200 		//get horizontal movement
1201 		tmpdir[0] = move.endpos[0] - ms->origin[0];
1202 		tmpdir[1] = move.endpos[1] - ms->origin[1];
1203 		tmpdir[2] = 0;
1204 		//
1205 		//AAS_DrawCross(move.endpos, 4, LINECOLOR_BLUE);
1206 		//the bot is blocked by something
1207 		if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse;
1208 		//perform the movement
1209 		if (type & MOVE_JUMP) EA_Jump(ms->client);
1210 		if (type & MOVE_CROUCH) EA_Crouch(ms->client);
1211 		EA_Move(ms->client, hordir, speed);
1212 		//movement was succesfull
1213 		return qtrue;
1214 	} //end if
1215 	else
1216 	{
1217 		if (ms->moveflags & MFL_BARRIERJUMP)
1218 		{
1219 			//if near the top or going down
1220 			if (ms->velocity[2] < 50)
1221 			{
1222 				EA_Move(ms->client, dir, speed);
1223 			} //end if
1224 		} //end if
1225 		//FIXME: do air control to avoid hazards
1226 		return qtrue;
1227 	} //end else
1228 } //end of the function BotWalkInDirection
1229 //===========================================================================
1230 //
1231 // Parameter:			-
1232 // Returns:				-
1233 // Changes Globals:		-
1234 //===========================================================================
BotMoveInDirection(int movestate,vec3_t dir,float speed,int type)1235 int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type)
1236 {
1237 	bot_movestate_t *ms;
1238 
1239 	ms = BotMoveStateFromHandle(movestate);
1240 	if (!ms) return qfalse;
1241 	//if swimming
1242 	if (AAS_Swimming(ms->origin))
1243 	{
1244 		return BotSwimInDirection(ms, dir, speed, type);
1245 	} //end if
1246 	else
1247 	{
1248 		return BotWalkInDirection(ms, dir, speed, type);
1249 	} //end else
1250 } //end of the function BotMoveInDirection
1251 //===========================================================================
1252 //
1253 // Parameter:			-
1254 // Returns:				-
1255 // Changes Globals:		-
1256 //===========================================================================
Intersection(vec2_t p1,vec2_t p2,vec2_t p3,vec2_t p4,vec2_t out)1257 int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out)
1258 {
1259    float x1, dx1, dy1, x2, dx2, dy2, d;
1260 
1261    dx1 = p2[0] - p1[0];
1262    dy1 = p2[1] - p1[1];
1263    dx2 = p4[0] - p3[0];
1264    dy2 = p4[1] - p3[1];
1265 
1266    d = dy1 * dx2 - dx1 * dy2;
1267    if (d != 0)
1268    {
1269       x1 = p1[1] * dx1 - p1[0] * dy1;
1270       x2 = p3[1] * dx2 - p3[0] * dy2;
1271       out[0] = (int) ((dx1 * x2 - dx2 * x1) / d);
1272       out[1] = (int) ((dy1 * x2 - dy2 * x1) / d);
1273 		return qtrue;
1274    } //end if
1275    else
1276    {
1277       return qfalse;
1278    } //end else
1279 } //end of the function Intersection
1280 //===========================================================================
1281 //
1282 // Parameter:			-
1283 // Returns:				-
1284 // Changes Globals:		-
1285 //===========================================================================
BotCheckBlocked(bot_movestate_t * ms,vec3_t dir,int checkbottom,bot_moveresult_t * result)1286 void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result)
1287 {
1288 	vec3_t mins, maxs, end, up = {0, 0, 1};
1289 	bsp_trace_t trace;
1290 
1291 	//test for entities obstructing the bot's path
1292 	AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
1293 	//
1294 	if (fabs(DotProduct(dir, up)) < 0.7)
1295 	{
1296 		mins[2] += sv_maxstep->value; //if the bot can step on
1297 		maxs[2] -= 10; //a little lower to avoid low ceiling
1298 	} //end if
1299 	VectorMA(ms->origin, 3, dir, end);
1300 	trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY);
1301 	//if not started in solid and not hitting the world entity
1302 	if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
1303 	{
1304 		result->blocked = qtrue;
1305 		result->blockentity = trace.ent;
1306 #ifdef DEBUG
1307 		//botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client);
1308 #endif //DEBUG
1309 	} //end if
1310 	//if not in an area with reachability
1311 	else if (checkbottom && !AAS_AreaReachability(ms->areanum))
1312 	{
1313 		//check if the bot is standing on something
1314 		AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
1315 		VectorMA(ms->origin, -3, up, end);
1316 		trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
1317 		if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
1318 		{
1319 			result->blocked = qtrue;
1320 			result->blockentity = trace.ent;
1321 			result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
1322 #ifdef DEBUG
1323 			//botimport.Print(PRT_MESSAGE, "%d: BotCheckBlocked: I'm blocked\n", ms->client);
1324 #endif //DEBUG
1325 		} //end if
1326 	} //end else
1327 } //end of the function BotCheckBlocked
1328 //===========================================================================
1329 //
1330 // Parameter:			-
1331 // Returns:				-
1332 // Changes Globals:		-
1333 //===========================================================================
BotTravel_Walk(bot_movestate_t * ms,aas_reachability_t * reach)1334 bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
1335 {
1336 	float dist, speed;
1337 	vec3_t hordir;
1338 	bot_moveresult_t_cleared( result );
1339 
1340 	//first walk straight to the reachability start
1341 	hordir[0] = reach->start[0] - ms->origin[0];
1342 	hordir[1] = reach->start[1] - ms->origin[1];
1343 	hordir[2] = 0;
1344 	dist = VectorNormalize(hordir);
1345 	//
1346 	BotCheckBlocked(ms, hordir, qtrue, &result);
1347 	//
1348 	if (dist < 10)
1349 	{
1350 		//walk straight to the reachability end
1351 		hordir[0] = reach->end[0] - ms->origin[0];
1352 		hordir[1] = reach->end[1] - ms->origin[1];
1353 		hordir[2] = 0;
1354 		dist = VectorNormalize(hordir);
1355 	} //end if
1356 	//if going towards a crouch area
1357 	if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL))
1358 	{
1359 		//if pretty close to the reachable area
1360 		if (dist < 20) EA_Crouch(ms->client);
1361 	} //end if
1362 	//
1363 	dist = BotGapDistance(ms->origin, hordir, ms->entitynum);
1364 	//
1365 	if (ms->moveflags & MFL_WALK)
1366 	{
1367 		if (dist > 0) speed = 200 - (180 - 1 * dist);
1368 		else speed = 200;
1369 		EA_Walk(ms->client);
1370 	} //end if
1371 	else
1372 	{
1373 		if (dist > 0) speed = 400 - (360 - 2 * dist);
1374 		else speed = 400;
1375 	} //end else
1376 	//elemantary action move in direction
1377 	EA_Move(ms->client, hordir, speed);
1378 	VectorCopy(hordir, result.movedir);
1379 	//
1380 	return result;
1381 } //end of the function BotTravel_Walk
1382 //===========================================================================
1383 //
1384 // Parameter:			-
1385 // Returns:				-
1386 // Changes Globals:		-
1387 //===========================================================================
BotFinishTravel_Walk(bot_movestate_t * ms,aas_reachability_t * reach)1388 bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
1389 {
1390 	vec3_t hordir;
1391 	float dist, speed;
1392 	bot_moveresult_t_cleared( result );
1393 	//if not on the ground and changed areas... don't walk back!!
1394 	//(doesn't seem to help)
1395 	/*
1396 	ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
1397 	if (ms->areanum == reach->areanum)
1398 	{
1399 #ifdef DEBUG
1400 		botimport.Print(PRT_MESSAGE, "BotFinishTravel_Walk: already in reach area\n");
1401 #endif //DEBUG
1402 		return result;
1403 	} //end if*/
1404 	//go straight to the reachability end
1405 	hordir[0] = reach->end[0] - ms->origin[0];
1406 	hordir[1] = reach->end[1] - ms->origin[1];
1407 	hordir[2] = 0;
1408 	dist = VectorNormalize(hordir);
1409 	//
1410 	if (dist > 100) dist = 100;
1411 	speed = 400 - (400 - 3 * dist);
1412 	//
1413 	EA_Move(ms->client, hordir, speed);
1414 	VectorCopy(hordir, result.movedir);
1415 	//
1416 	return result;
1417 } //end of the function BotFinishTravel_Walk
1418 //===========================================================================
1419 //
1420 // Parameter:				-
1421 // Returns:					-
1422 // Changes Globals:		-
1423 //===========================================================================
BotTravel_Crouch(bot_movestate_t * ms,aas_reachability_t * reach)1424 bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach)
1425 {
1426 	float speed;
1427 	vec3_t hordir;
1428 	bot_moveresult_t_cleared( result );
1429 
1430 	//
1431 	speed = 400;
1432 	//walk straight to reachability end
1433 	hordir[0] = reach->end[0] - ms->origin[0];
1434 	hordir[1] = reach->end[1] - ms->origin[1];
1435 	hordir[2] = 0;
1436 	VectorNormalize(hordir);
1437 	//
1438 	BotCheckBlocked(ms, hordir, qtrue, &result);
1439 	//elemantary actions
1440 	EA_Crouch(ms->client);
1441 	EA_Move(ms->client, hordir, speed);
1442 	//
1443 	VectorCopy(hordir, result.movedir);
1444 	//
1445 	return result;
1446 } //end of the function BotTravel_Crouch
1447 //===========================================================================
1448 //
1449 // Parameter:				-
1450 // Returns:					-
1451 // Changes Globals:		-
1452 //===========================================================================
BotTravel_BarrierJump(bot_movestate_t * ms,aas_reachability_t * reach)1453 bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
1454 {
1455 	float dist, speed;
1456 	vec3_t hordir;
1457 	bot_moveresult_t_cleared( result );
1458 
1459 	//walk straight to reachability start
1460 	hordir[0] = reach->start[0] - ms->origin[0];
1461 	hordir[1] = reach->start[1] - ms->origin[1];
1462 	hordir[2] = 0;
1463 	dist = VectorNormalize(hordir);
1464 	//
1465 	BotCheckBlocked(ms, hordir, qtrue, &result);
1466 	//if pretty close to the barrier
1467 	if (dist < 9)
1468 	{
1469 		EA_Jump(ms->client);
1470 	} //end if
1471 	else
1472 	{
1473 		if (dist > 60) dist = 60;
1474 		speed = 360 - (360 - 6 * dist);
1475 		EA_Move(ms->client, hordir, speed);
1476 	} //end else
1477 	VectorCopy(hordir, result.movedir);
1478 	//
1479 	return result;
1480 } //end of the function BotTravel_BarrierJump
1481 //===========================================================================
1482 //
1483 // Parameter:				-
1484 // Returns:					-
1485 // Changes Globals:		-
1486 //===========================================================================
BotFinishTravel_BarrierJump(bot_movestate_t * ms,aas_reachability_t * reach)1487 bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
1488 {
1489 	vec3_t hordir;
1490 	bot_moveresult_t_cleared( result );
1491 
1492 	//if near the top or going down
1493 	if (ms->velocity[2] < 250)
1494 	{
1495 		hordir[0] = reach->end[0] - ms->origin[0];
1496 		hordir[1] = reach->end[1] - ms->origin[1];
1497 		hordir[2] = 0;
1498 		//
1499 		BotCheckBlocked(ms, hordir, qtrue, &result);
1500 		//
1501 		EA_Move(ms->client, hordir, 400);
1502 		VectorCopy(hordir, result.movedir);
1503 	} //end if
1504 	//
1505 	return result;
1506 } //end of the function BotFinishTravel_BarrierJump
1507 //===========================================================================
1508 //
1509 // Parameter:				-
1510 // Returns:					-
1511 // Changes Globals:		-
1512 //===========================================================================
BotTravel_Swim(bot_movestate_t * ms,aas_reachability_t * reach)1513 bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach)
1514 {
1515 	vec3_t dir;
1516 	bot_moveresult_t_cleared( result );
1517 
1518 	//swim straight to reachability end
1519 	VectorSubtract(reach->start, ms->origin, dir);
1520 	VectorNormalize(dir);
1521 	//
1522 	BotCheckBlocked(ms, dir, qtrue, &result);
1523 	//elemantary actions
1524 	EA_Move(ms->client, dir, 400);
1525 	//
1526 	VectorCopy(dir, result.movedir);
1527 	Vector2Angles(dir, result.ideal_viewangles);
1528 	result.flags |= MOVERESULT_SWIMVIEW;
1529 	//
1530 	return result;
1531 } //end of the function BotTravel_Swim
1532 //===========================================================================
1533 //
1534 // Parameter:				-
1535 // Returns:					-
1536 // Changes Globals:		-
1537 //===========================================================================
BotTravel_WaterJump(bot_movestate_t * ms,aas_reachability_t * reach)1538 bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
1539 {
1540 	vec3_t dir, hordir;
1541 	float dist;
1542 	bot_moveresult_t_cleared( result );
1543 
1544 	//swim straight to reachability end
1545 	VectorSubtract(reach->end, ms->origin, dir);
1546 	VectorCopy(dir, hordir);
1547 	hordir[2] = 0;
1548 	dir[2] += 15 + Q_flrand(-1.0f, 1.0f) * 40;
1549 	//botimport.Print(PRT_MESSAGE, "BotTravel_WaterJump: dir[2] = %f\n", dir[2]);
1550 	VectorNormalize(dir);
1551 	dist = VectorNormalize(hordir);
1552 	//elemantary actions
1553 	//EA_Move(ms->client, dir, 400);
1554 	EA_MoveForward(ms->client);
1555 	//move up if close to the actual out of water jump spot
1556 	if (dist < 40) EA_MoveUp(ms->client);
1557 	//set the ideal view angles
1558 	Vector2Angles(dir, result.ideal_viewangles);
1559 	result.flags |= MOVERESULT_MOVEMENTVIEW;
1560 	//
1561 	VectorCopy(dir, result.movedir);
1562 	//
1563 	return result;
1564 } //end of the function BotTravel_WaterJump
1565 //===========================================================================
1566 //
1567 // Parameter:				-
1568 // Returns:					-
1569 // Changes Globals:		-
1570 //===========================================================================
BotFinishTravel_WaterJump(bot_movestate_t * ms,aas_reachability_t * reach)1571 bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
1572 {
1573 	vec3_t dir, pnt;
1574 	bot_moveresult_t_cleared( result );
1575 
1576 	//botimport.Print(PRT_MESSAGE, "BotFinishTravel_WaterJump\n");
1577 	//if waterjumping there's nothing to do
1578 	if (ms->moveflags & MFL_WATERJUMP) return result;
1579 	//if not touching any water anymore don't do anything
1580 	//otherwise the bot sometimes keeps jumping?
1581 	VectorCopy(ms->origin, pnt);
1582 	pnt[2] -= 32;	//extra for q2dm4 near red armor/mega health
1583 	if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result;
1584 	//swim straight to reachability end
1585 	VectorSubtract(reach->end, ms->origin, dir);
1586 	dir[0] += Q_flrand(-1.0f, 1.0f) * 10;
1587 	dir[1] += Q_flrand(-1.0f, 1.0f) * 10;
1588 	dir[2] += 70 + Q_flrand(-1.0f, 1.0f) * 10;
1589 	//elemantary actions
1590 	EA_Move(ms->client, dir, 400);
1591 	//set the ideal view angles
1592 	Vector2Angles(dir, result.ideal_viewangles);
1593 	result.flags |= MOVERESULT_MOVEMENTVIEW;
1594 	//
1595 	VectorCopy(dir, result.movedir);
1596 	//
1597 	return result;
1598 } //end of the function BotFinishTravel_WaterJump
1599 //===========================================================================
1600 //
1601 // Parameter:				-
1602 // Returns:					-
1603 // Changes Globals:		-
1604 //===========================================================================
BotTravel_WalkOffLedge(bot_movestate_t * ms,aas_reachability_t * reach)1605 bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
1606 {
1607 	vec3_t hordir, dir;
1608 	float dist, speed, reachhordist;
1609 	bot_moveresult_t_cleared( result );
1610 
1611 	//check if the bot is blocked by anything
1612 	VectorSubtract(reach->start, ms->origin, dir);
1613 	VectorNormalize(dir);
1614 	BotCheckBlocked(ms, dir, qtrue, &result);
1615 	//if the reachability start and end are practially above each other
1616 	VectorSubtract(reach->end, reach->start, dir);
1617 	dir[2] = 0;
1618 	reachhordist = VectorLength(dir);
1619 	//walk straight to the reachability start
1620 	hordir[0] = reach->start[0] - ms->origin[0];
1621 	hordir[1] = reach->start[1] - ms->origin[1];
1622 	hordir[2] = 0;
1623 	dist = VectorNormalize(hordir);
1624 	//if pretty close to the start focus on the reachability end
1625 	if (dist < 48)
1626 	{
1627 		hordir[0] = reach->end[0] - ms->origin[0];
1628 		hordir[1] = reach->end[1] - ms->origin[1];
1629 		hordir[2] = 0;
1630 		VectorNormalize(hordir);
1631 		//
1632 		if (reachhordist < 20)
1633 		{
1634 			speed = 100;
1635 		} //end if
1636 		else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed))
1637 		{
1638 			speed = 400;
1639 		} //end if
1640 	} //end if
1641 	else
1642 	{
1643 		if (reachhordist < 20)
1644 		{
1645 			if (dist > 64) dist = 64;
1646 			speed = 400 - (256 - 4 * dist);
1647 		} //end if
1648 		else
1649 		{
1650 			speed = 400;
1651 		} //end else
1652 	} //end else
1653 	//
1654 	BotCheckBlocked(ms, hordir, qtrue, &result);
1655 	//elemantary action
1656 	EA_Move(ms->client, hordir, speed);
1657 	VectorCopy(hordir, result.movedir);
1658 	//
1659 	return result;
1660 } //end of the function BotTravel_WalkOffLedge
1661 //===========================================================================
1662 //
1663 // Parameter:			-
1664 // Returns:				-
1665 // Changes Globals:		-
1666 //===========================================================================
BotAirControl(vec3_t origin,vec3_t velocity,vec3_t goal,vec3_t dir,float * speed)1667 int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed)
1668 {
1669 	vec3_t org, vel;
1670 	float dist;
1671 	int i;
1672 
1673 	VectorCopy(origin, org);
1674 	VectorScale(velocity, 0.1, vel);
1675 	for (i = 0; i < 50; i++)
1676 	{
1677 		vel[2] -= sv_gravity->value * 0.01;
1678 		//if going down and next position would be below the goal
1679 		if (vel[2] < 0 && org[2] + vel[2] < goal[2])
1680 		{
1681 			VectorScale(vel, (goal[2] - org[2]) / vel[2], vel);
1682 			VectorAdd(org, vel, org);
1683 			VectorSubtract(goal, org, dir);
1684 			dist = VectorNormalize(dir);
1685 			if (dist > 32) dist = 32;
1686 			*speed = 400 - (400 - 13 * dist);
1687 			return qtrue;
1688 		} //end if
1689 		else
1690 		{
1691 			VectorAdd(org, vel, org);
1692 		} //end else
1693 	} //end for
1694 	VectorSet(dir, 0, 0, 0);
1695 	*speed = 400;
1696 	return qfalse;
1697 } //end of the function BotAirControl
1698 //===========================================================================
1699 //
1700 // Parameter:				-
1701 // Returns:					-
1702 // Changes Globals:		-
1703 //===========================================================================
BotFinishTravel_WalkOffLedge(bot_movestate_t * ms,aas_reachability_t * reach)1704 bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
1705 {
1706 	vec3_t dir, hordir, end, v;
1707 	float dist, speed;
1708 	bot_moveresult_t_cleared( result );
1709 
1710 	//
1711 	VectorSubtract(reach->end, ms->origin, dir);
1712 	BotCheckBlocked(ms, dir, qtrue, &result);
1713 	//
1714 	VectorSubtract(reach->end, ms->origin, v);
1715 	v[2] = 0;
1716 	dist = VectorNormalize(v);
1717 	if (dist > 16) VectorMA(reach->end, 16, v, end);
1718 	else VectorCopy(reach->end, end);
1719 	//
1720 	if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed))
1721 	{
1722 		//go straight to the reachability end
1723 		VectorCopy(dir, hordir);
1724 		hordir[2] = 0;
1725 		//
1726 		speed = 400;
1727 	} //end if
1728 	//
1729 	EA_Move(ms->client, hordir, speed);
1730 	VectorCopy(hordir, result.movedir);
1731 	//
1732 	return result;
1733 } //end of the function BotFinishTravel_WalkOffLedge
1734 //===========================================================================
1735 //
1736 // Parameter:				-
1737 // Returns:					-
1738 // Changes Globals:		-
1739 //===========================================================================
1740 /*
1741 bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
1742 {
1743 	vec3_t hordir;
1744 	float dist, gapdist, speed, horspeed, sv_jumpvel;
1745 	bot_moveresult_t_cleared( result );
1746 
1747 	//
1748 	sv_jumpvel = botlibglobals.sv_jumpvel->value;
1749 	//walk straight to the reachability start
1750 	hordir[0] = reach->start[0] - ms->origin[0];
1751 	hordir[1] = reach->start[1] - ms->origin[1];
1752 	hordir[2] = 0;
1753 	dist = VectorNormalize(hordir);
1754 	//
1755 	speed = 350;
1756 	//
1757 	gapdist = BotGapDistance(ms, hordir, ms->entitynum);
1758 	//if pretty close to the start focus on the reachability end
1759 	if (dist < 50 || (gapdist && gapdist < 50))
1760 	{
1761 		//NOTE: using max speed (400) works best
1762 		//if (AAS_HorizontalVelocityForJump(sv_jumpvel, ms->origin, reach->end, &horspeed))
1763 		//{
1764 		//	speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;
1765 		//} //end if
1766 		hordir[0] = reach->end[0] - ms->origin[0];
1767 		hordir[1] = reach->end[1] - ms->origin[1];
1768 		VectorNormalize(hordir);
1769 		//elemantary action jump
1770 		EA_Jump(ms->client);
1771 		//
1772 		ms->jumpreach = ms->lastreachnum;
1773 		speed = 600;
1774 	} //end if
1775 	else
1776 	{
1777 		if (AAS_HorizontalVelocityForJump(sv_jumpvel, reach->start, reach->end, &horspeed))
1778 		{
1779 			speed = horspeed * 400 / botlibglobals.sv_maxwalkvelocity->value;
1780 		} //end if
1781 	} //end else
1782 	//elemantary action
1783 	EA_Move(ms->client, hordir, speed);
1784 	VectorCopy(hordir, result.movedir);
1785 	//
1786 	return result;
1787 } //end of the function BotTravel_Jump*/
1788 /*
1789 bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
1790 {
1791 	vec3_t hordir, dir1, dir2, mins, maxs, start, end;
1792 	int gapdist;
1793 	float dist1, dist2, speed;
1794 	bot_moveresult_t_cleared( result );
1795 	bsp_trace_t trace;
1796 
1797 	//
1798 	hordir[0] = reach->start[0] - reach->end[0];
1799 	hordir[1] = reach->start[1] - reach->end[1];
1800 	hordir[2] = 0;
1801 	VectorNormalize(hordir);
1802 	//
1803 	VectorCopy(reach->start, start);
1804 	start[2] += 1;
1805 	//minus back the bouding box size plus 16
1806 	VectorMA(reach->start, 80, hordir, end);
1807 	//
1808 	AAS_PresenceTypeBoundingBox(PRESENCE_NORMAL, mins, maxs);
1809 	//check for solids
1810 	trace = AAS_Trace(start, mins, maxs, end, ms->entitynum, MASK_PLAYERSOLID);
1811 	if (trace.startsolid) VectorCopy(start, trace.endpos);
1812 	//check for a gap
1813 	for (gapdist = 0; gapdist < 80; gapdist += 10)
1814 	{
1815 		VectorMA(start, gapdist+10, hordir, end);
1816 		end[2] += 1;
1817 		if (AAS_PointAreaNum(end) != ms->reachareanum) break;
1818 	} //end for
1819 	if (gapdist < 80) VectorMA(reach->start, gapdist, hordir, trace.endpos);
1820 //	dist1 = BotGapDistance(start, hordir, ms->entitynum);
1821 //	if (dist1 && dist1 <= trace.fraction * 80) VectorMA(reach->start, dist1-20, hordir, trace.endpos);
1822 	//
1823 	VectorSubtract(ms->origin, reach->start, dir1);
1824 	dir1[2] = 0;
1825 	dist1 = VectorNormalize(dir1);
1826 	VectorSubtract(ms->origin, trace.endpos, dir2);
1827 	dir2[2] = 0;
1828 	dist2 = VectorNormalize(dir2);
1829 	//if just before the reachability start
1830 	if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
1831 	{
1832 		//botimport.Print(PRT_MESSAGE, "between jump start and run to point\n");
1833 		hordir[0] = reach->end[0] - ms->origin[0];
1834 		hordir[1] = reach->end[1] - ms->origin[1];
1835 		hordir[2] = 0;
1836 		VectorNormalize(hordir);
1837 		//elemantary action jump
1838 		if (dist1 < 24) EA_Jump(ms->client);
1839 		else if (dist1 < 32) EA_DelayedJump(ms->client);
1840 		EA_Move(ms->client, hordir, 600);
1841 		//
1842 		ms->jumpreach = ms->lastreachnum;
1843 	} //end if
1844 	else
1845 	{
1846 		//botimport.Print(PRT_MESSAGE, "going towards run to point\n");
1847 		hordir[0] = trace.endpos[0] - ms->origin[0];
1848 		hordir[1] = trace.endpos[1] - ms->origin[1];
1849 		hordir[2] = 0;
1850 		VectorNormalize(hordir);
1851 		//
1852 		if (dist2 > 80) dist2 = 80;
1853 		speed = 400 - (400 - 5 * dist2);
1854 		EA_Move(ms->client, hordir, speed);
1855 	} //end else
1856 	VectorCopy(hordir, result.movedir);
1857 	//
1858 	return result;
1859 } //end of the function BotTravel_Jump*/
1860 //*
BotTravel_Jump(bot_movestate_t * ms,aas_reachability_t * reach)1861 bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
1862 {
1863 	vec3_t hordir, dir1, dir2, start, end, runstart;
1864 //	vec3_t runstart, dir1, dir2, hordir;
1865 	int gapdist;
1866 	float dist1, dist2, speed;
1867 	bot_moveresult_t_cleared( result );
1868 
1869 	//
1870 	AAS_JumpReachRunStart(reach, runstart);
1871 	//*
1872 	hordir[0] = runstart[0] - reach->start[0];
1873 	hordir[1] = runstart[1] - reach->start[1];
1874 	hordir[2] = 0;
1875 	VectorNormalize(hordir);
1876 	//
1877 	VectorCopy(reach->start, start);
1878 	start[2] += 1;
1879 	VectorMA(reach->start, 80, hordir, runstart);
1880 	//check for a gap
1881 	for (gapdist = 0; gapdist < 80; gapdist += 10)
1882 	{
1883 		VectorMA(start, gapdist+10, hordir, end);
1884 		end[2] += 1;
1885 		if (AAS_PointAreaNum(end) != ms->reachareanum) break;
1886 	} //end for
1887 	if (gapdist < 80) VectorMA(reach->start, gapdist, hordir, runstart);
1888 	//
1889 	VectorSubtract(ms->origin, reach->start, dir1);
1890 	dir1[2] = 0;
1891 	dist1 = VectorNormalize(dir1);
1892 	VectorSubtract(ms->origin, runstart, dir2);
1893 	dir2[2] = 0;
1894 	dist2 = VectorNormalize(dir2);
1895 	//if just before the reachability start
1896 	if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
1897 	{
1898 //		botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
1899 		hordir[0] = reach->end[0] - ms->origin[0];
1900 		hordir[1] = reach->end[1] - ms->origin[1];
1901 		hordir[2] = 0;
1902 		VectorNormalize(hordir);
1903 		//elemantary action jump
1904 		if (dist1 < 24) EA_Jump(ms->client);
1905 		else if (dist1 < 32) EA_DelayedJump(ms->client);
1906 		EA_Move(ms->client, hordir, 600);
1907 		//
1908 		ms->jumpreach = ms->lastreachnum;
1909 	} //end if
1910 	else
1911 	{
1912 //		botimport.Print(PRT_MESSAGE, "going towards run start point\n");
1913 		hordir[0] = runstart[0] - ms->origin[0];
1914 		hordir[1] = runstart[1] - ms->origin[1];
1915 		hordir[2] = 0;
1916 		VectorNormalize(hordir);
1917 		//
1918 		if (dist2 > 80) dist2 = 80;
1919 		speed = 400 - (400 - 5 * dist2);
1920 		EA_Move(ms->client, hordir, speed);
1921 	} //end else
1922 	VectorCopy(hordir, result.movedir);
1923 	//
1924 	return result;
1925 } //end of the function BotTravel_Jump*/
1926 //===========================================================================
1927 //
1928 // Parameter:				-
1929 // Returns:					-
1930 // Changes Globals:		-
1931 //===========================================================================
BotFinishTravel_Jump(bot_movestate_t * ms,aas_reachability_t * reach)1932 bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
1933 {
1934 	vec3_t hordir, hordir2;
1935 	float speed, dist;
1936 	bot_moveresult_t_cleared( result );
1937 
1938 	//if not jumped yet
1939 	if (!ms->jumpreach) return result;
1940 	//go straight to the reachability end
1941 	hordir[0] = reach->end[0] - ms->origin[0];
1942 	hordir[1] = reach->end[1] - ms->origin[1];
1943 	hordir[2] = 0;
1944 	dist = VectorNormalize(hordir);
1945 	//
1946 	hordir2[0] = reach->end[0] - reach->start[0];
1947 	hordir2[1] = reach->end[1] - reach->start[1];
1948 	hordir2[2] = 0;
1949 	VectorNormalize(hordir2);
1950 	//
1951 	if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result;
1952 	//always use max speed when traveling through the air
1953 	speed = 800;
1954 	//
1955 	EA_Move(ms->client, hordir, speed);
1956 	VectorCopy(hordir, result.movedir);
1957 	//
1958 	return result;
1959 } //end of the function BotFinishTravel_Jump
1960 //===========================================================================
1961 //
1962 // Parameter:				-
1963 // Returns:					-
1964 // Changes Globals:		-
1965 //===========================================================================
BotTravel_Ladder(bot_movestate_t * ms,aas_reachability_t * reach)1966 bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach)
1967 {
1968 	//float dist, speed;
1969 	vec3_t dir, viewdir;//, hordir;
1970 	vec3_t origin = {0, 0, 0};
1971 //	vec3_t up = {0, 0, 1};
1972 	bot_moveresult_t_cleared( result );
1973 
1974 	//
1975 //	if ((ms->moveflags & MFL_AGAINSTLADDER))
1976 		//NOTE: not a good idea for ladders starting in water
1977 		// || !(ms->moveflags & MFL_ONGROUND))
1978 	{
1979 		//botimport.Print(PRT_MESSAGE, "against ladder or not on ground\n");
1980 		VectorSubtract(reach->end, ms->origin, dir);
1981 		VectorNormalize(dir);
1982 		//set the ideal view angles, facing the ladder up or down
1983 		viewdir[0] = dir[0];
1984 		viewdir[1] = dir[1];
1985 		viewdir[2] = 3 * dir[2];
1986 		Vector2Angles(viewdir, result.ideal_viewangles);
1987 		//elemantary action
1988 		EA_Move(ms->client, origin, 0);
1989 		EA_MoveForward(ms->client);
1990 		//set movement view flag so the AI can see the view is focussed
1991 		result.flags |= MOVERESULT_MOVEMENTVIEW;
1992 	} //end if
1993 /*	else
1994 	{
1995 		//botimport.Print(PRT_MESSAGE, "moving towards ladder\n");
1996 		VectorSubtract(reach->end, ms->origin, dir);
1997 		//make sure the horizontal movement is large enough
1998 		VectorCopy(dir, hordir);
1999 		hordir[2] = 0;
2000 		dist = VectorNormalize(hordir);
2001 		//
2002 		dir[0] = hordir[0];
2003 		dir[1] = hordir[1];
2004 		if (dir[2] > 0) dir[2] = 1;
2005 		else dir[2] = -1;
2006 		if (dist > 50) dist = 50;
2007 		speed = 400 - (200 - 4 * dist);
2008 		EA_Move(ms->client, dir, speed);
2009 	} //end else*/
2010 	//save the movement direction
2011 	VectorCopy(dir, result.movedir);
2012 	//
2013 	return result;
2014 } //end of the function BotTravel_Ladder
2015 //===========================================================================
2016 //
2017 // Parameter:				-
2018 // Returns:					-
2019 // Changes Globals:		-
2020 //===========================================================================
BotTravel_Teleport(bot_movestate_t * ms,aas_reachability_t * reach)2021 bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach)
2022 {
2023 	vec3_t hordir;
2024 	float dist;
2025 	bot_moveresult_t_cleared( result );
2026 
2027 	//if the bot is being teleported
2028 	if (ms->moveflags & MFL_TELEPORTED) return result;
2029 
2030 	//walk straight to center of the teleporter
2031 	VectorSubtract(reach->start, ms->origin, hordir);
2032 	if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
2033 	dist = VectorNormalize(hordir);
2034 	//
2035 	BotCheckBlocked(ms, hordir, qtrue, &result);
2036 
2037 	if (dist < 30) EA_Move(ms->client, hordir, 200);
2038 	else EA_Move(ms->client, hordir, 400);
2039 
2040 	if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2041 
2042 	VectorCopy(hordir, result.movedir);
2043 	return result;
2044 } //end of the function BotTravel_Teleport
2045 //===========================================================================
2046 //
2047 // Parameter:				-
2048 // Returns:					-
2049 // Changes Globals:		-
2050 //===========================================================================
BotTravel_Elevator(bot_movestate_t * ms,aas_reachability_t * reach)2051 bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
2052 {
2053 	vec3_t dir, dir1, dir2, hordir, bottomcenter;
2054 	float dist, dist1, dist2, speed;
2055 	bot_moveresult_t_cleared( result );
2056 
2057 	//if standing on the plat
2058 	if (BotOnMover(ms->origin, ms->entitynum, reach))
2059 	{
2060 #ifdef DEBUG_ELEVATOR
2061 		botimport.Print(PRT_MESSAGE, "bot on elevator\n");
2062 #endif //DEBUG_ELEVATOR
2063 		//if vertically not too far from the end point
2064 		if (fabs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value)
2065 		{
2066 #ifdef DEBUG_ELEVATOR
2067 			botimport.Print(PRT_MESSAGE, "bot moving to end\n");
2068 #endif //DEBUG_ELEVATOR
2069 			//move to the end point
2070 			VectorSubtract(reach->end, ms->origin, hordir);
2071 			hordir[2] = 0;
2072 			VectorNormalize(hordir);
2073 			if (!BotCheckBarrierJump(ms, hordir, 100))
2074 			{
2075 				EA_Move(ms->client, hordir, 400);
2076 			} //end if
2077 			VectorCopy(hordir, result.movedir);
2078 		} //end else
2079 		//if not really close to the center of the elevator
2080 		else
2081 		{
2082 			MoverBottomCenter(reach, bottomcenter);
2083 			VectorSubtract(bottomcenter, ms->origin, hordir);
2084 			hordir[2] = 0;
2085 			dist = VectorNormalize(hordir);
2086 			//
2087 			if (dist > 10)
2088 			{
2089 #ifdef DEBUG_ELEVATOR
2090 				botimport.Print(PRT_MESSAGE, "bot moving to center\n");
2091 #endif //DEBUG_ELEVATOR
2092 				//move to the center of the plat
2093 				if (dist > 100) dist = 100;
2094 				speed = 400 - (400 - 4 * dist);
2095 				//
2096 				EA_Move(ms->client, hordir, speed);
2097 				VectorCopy(hordir, result.movedir);
2098 			} //end if
2099 		} //end else
2100 	} //end if
2101 	else
2102 	{
2103 #ifdef DEBUG_ELEVATOR
2104 		botimport.Print(PRT_MESSAGE, "bot not on elevator\n");
2105 #endif //DEBUG_ELEVATOR
2106 		//if very near the reachability end
2107 		VectorSubtract(reach->end, ms->origin, dir);
2108 		dist = VectorLength(dir);
2109 		if (dist < 64)
2110 		{
2111 			if (dist > 60) dist = 60;
2112 			speed = 360 - (360 - 6 * dist);
2113 			//
2114 			if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
2115 			{
2116 				if (speed > 5) EA_Move(ms->client, dir, speed);
2117 			} //end if
2118 			VectorCopy(dir, result.movedir);
2119 			//
2120 			if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2121 			//stop using this reachability
2122 			ms->reachability_time = 0;
2123 			return result;
2124 		} //end if
2125 		//get direction and distance to reachability start
2126 		VectorSubtract(reach->start, ms->origin, dir1);
2127 		if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
2128 		dist1 = VectorNormalize(dir1);
2129 		//if the elevator isn't down
2130 		if (!MoverDown(reach))
2131 		{
2132 #ifdef DEBUG_ELEVATOR
2133 			botimport.Print(PRT_MESSAGE, "elevator not down\n");
2134 #endif //DEBUG_ELEVATOR
2135 			dist = dist1;
2136 			VectorCopy(dir1, dir);
2137 			//
2138 			BotCheckBlocked(ms, dir, qfalse, &result);
2139 			//
2140 			if (dist > 60) dist = 60;
2141 			speed = 360 - (360 - 6 * dist);
2142 			//
2143 			if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
2144 			{
2145 				if (speed > 5) EA_Move(ms->client, dir, speed);
2146 			} //end if
2147 			VectorCopy(dir, result.movedir);
2148 			//
2149 			if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2150 			//this isn't a failure... just wait till the elevator comes down
2151 			result.type = RESULTTYPE_ELEVATORUP;
2152 			result.flags |= MOVERESULT_WAITING;
2153 			return result;
2154 		} //end if
2155 		//get direction and distance to elevator bottom center
2156 		MoverBottomCenter(reach, bottomcenter);
2157 		VectorSubtract(bottomcenter, ms->origin, dir2);
2158 		if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
2159 		dist2 = VectorNormalize(dir2);
2160 		//if very close to the reachability start or
2161 		//closer to the elevator center or
2162 		//between reachability start and elevator center
2163 		if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
2164 		{
2165 #ifdef DEBUG_ELEVATOR
2166 			botimport.Print(PRT_MESSAGE, "bot moving to center\n");
2167 #endif //DEBUG_ELEVATOR
2168 			dist = dist2;
2169 			VectorCopy(dir2, dir);
2170 		} //end if
2171 		else //closer to the reachability start
2172 		{
2173 #ifdef DEBUG_ELEVATOR
2174 			botimport.Print(PRT_MESSAGE, "bot moving to start\n");
2175 #endif //DEBUG_ELEVATOR
2176 			dist = dist1;
2177 			VectorCopy(dir1, dir);
2178 		} //end else
2179 		//
2180 		BotCheckBlocked(ms, dir, qfalse, &result);
2181 		//
2182 		if (dist > 60) dist = 60;
2183 		speed = 400 - (400 - 6 * dist);
2184 		//
2185 		if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
2186 		{
2187 			EA_Move(ms->client, dir, speed);
2188 		} //end if
2189 		VectorCopy(dir, result.movedir);
2190 		//
2191 		if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2192 	} //end else
2193 	return result;
2194 } //end of the function BotTravel_Elevator
2195 //===========================================================================
2196 //
2197 // Parameter:				-
2198 // Returns:					-
2199 // Changes Globals:		-
2200 //===========================================================================
BotFinishTravel_Elevator(bot_movestate_t * ms,aas_reachability_t * reach)2201 bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
2202 {
2203 	vec3_t bottomcenter, bottomdir, topdir;
2204 	bot_moveresult_t_cleared( result );
2205 
2206 	//
2207 	MoverBottomCenter(reach, bottomcenter);
2208 	VectorSubtract(bottomcenter, ms->origin, bottomdir);
2209 	//
2210 	VectorSubtract(reach->end, ms->origin, topdir);
2211 	//
2212 	if (fabs(bottomdir[2]) < fabs(topdir[2]))
2213 	{
2214 		VectorNormalize(bottomdir);
2215 		EA_Move(ms->client, bottomdir, 300);
2216 	} //end if
2217 	else
2218 	{
2219 		VectorNormalize(topdir);
2220 		EA_Move(ms->client, topdir, 300);
2221 	} //end else
2222 	return result;
2223 } //end of the function BotFinishTravel_Elevator
2224 //===========================================================================
2225 //
2226 // Parameter:			-
2227 // Returns:				-
2228 // Changes Globals:		-
2229 //===========================================================================
BotFuncBobStartEnd(aas_reachability_t * reach,vec3_t start,vec3_t end,vec3_t origin)2230 void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin)
2231 {
2232 	int spawnflags, modelnum;
2233 	vec3_t mins, maxs, mid, angles = {0, 0, 0};
2234 	int num0, num1;
2235 
2236 	modelnum = reach->facenum & 0x0000FFFF;
2237 	if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
2238 	{
2239 		botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum);
2240 		VectorSet(start, 0, 0, 0);
2241 		VectorSet(end, 0, 0, 0);
2242 		return;
2243 	} //end if
2244 	AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
2245 	VectorAdd(mins, maxs, mid);
2246 	VectorScale(mid, 0.5, mid);
2247 	VectorCopy(mid, start);
2248 	VectorCopy(mid, end);
2249 	spawnflags = reach->facenum >> 16;
2250 	num0 = reach->edgenum >> 16;
2251 	if (num0 > 0x00007FFF) num0 |= 0xFFFF0000;
2252 	num1 = reach->edgenum & 0x0000FFFF;
2253 	if (num1 > 0x00007FFF) num1 |= 0xFFFF0000;
2254 	if (spawnflags & 1)
2255 	{
2256 		start[0] = num0;
2257 		end[0] = num1;
2258 		//
2259 		origin[0] += mid[0];
2260 		origin[1] = mid[1];
2261 		origin[2] = mid[2];
2262 	} //end if
2263 	else if (spawnflags & 2)
2264 	{
2265 		start[1] = num0;
2266 		end[1] = num1;
2267 		//
2268 		origin[0] = mid[0];
2269 		origin[1] += mid[1];
2270 		origin[2] = mid[2];
2271 	} //end else if
2272 	else
2273 	{
2274 		start[2] = num0;
2275 		end[2] = num1;
2276 		//
2277 		origin[0] = mid[0];
2278 		origin[1] = mid[1];
2279 		origin[2] += mid[2];
2280 	} //end else
2281 } //end of the function BotFuncBobStartEnd
2282 //===========================================================================
2283 //
2284 // Parameter:			-
2285 // Returns:				-
2286 // Changes Globals:		-
2287 //===========================================================================
BotTravel_FuncBobbing(bot_movestate_t * ms,aas_reachability_t * reach)2288 bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
2289 {
2290 	vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin;
2291 	float dist, dist1, dist2, speed;
2292 	bot_moveresult_t_cleared( result );
2293 
2294 	//
2295 	BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
2296 	//if standing ontop of the func_bobbing
2297 	if (BotOnMover(ms->origin, ms->entitynum, reach))
2298 	{
2299 #ifdef DEBUG_FUNCBOB
2300 		botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n");
2301 #endif
2302 		//if near end point of reachability
2303 		VectorSubtract(bob_origin, bob_end, dir);
2304 		if (VectorLength(dir) < 24)
2305 		{
2306 #ifdef DEBUG_FUNCBOB
2307 			botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n");
2308 #endif
2309 			//move to the end point
2310 			VectorSubtract(reach->end, ms->origin, hordir);
2311 			hordir[2] = 0;
2312 			VectorNormalize(hordir);
2313 			if (!BotCheckBarrierJump(ms, hordir, 100))
2314 			{
2315 				EA_Move(ms->client, hordir, 400);
2316 			} //end if
2317 			VectorCopy(hordir, result.movedir);
2318 		} //end else
2319 		//if not really close to the center of the elevator
2320 		else
2321 		{
2322 			MoverBottomCenter(reach, bottomcenter);
2323 			VectorSubtract(bottomcenter, ms->origin, hordir);
2324 			hordir[2] = 0;
2325 			dist = VectorNormalize(hordir);
2326 			//
2327 			if (dist > 10)
2328 			{
2329 #ifdef DEBUG_FUNCBOB
2330 				botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
2331 #endif
2332 				//move to the center of the plat
2333 				if (dist > 100) dist = 100;
2334 				speed = 400 - (400 - 4 * dist);
2335 				//
2336 				EA_Move(ms->client, hordir, speed);
2337 				VectorCopy(hordir, result.movedir);
2338 			} //end if
2339 		} //end else
2340 	} //end if
2341 	else
2342 	{
2343 #ifdef DEBUG_FUNCBOB
2344 		botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n");
2345 #endif
2346 		//if very near the reachability end
2347 		VectorSubtract(reach->end, ms->origin, dir);
2348 		dist = VectorLength(dir);
2349 		if (dist < 64)
2350 		{
2351 #ifdef DEBUG_FUNCBOB
2352 			botimport.Print(PRT_MESSAGE, "bot moving to end\n");
2353 #endif
2354 			if (dist > 60) dist = 60;
2355 			speed = 360 - (360 - 6 * dist);
2356 			//if swimming or no barrier jump
2357 			if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
2358 			{
2359 				if (speed > 5) EA_Move(ms->client, dir, speed);
2360 			} //end if
2361 			VectorCopy(dir, result.movedir);
2362 			//
2363 			if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2364 			//stop using this reachability
2365 			ms->reachability_time = 0;
2366 			return result;
2367 		} //end if
2368 		//get direction and distance to reachability start
2369 		VectorSubtract(reach->start, ms->origin, dir1);
2370 		if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
2371 		dist1 = VectorNormalize(dir1);
2372 		//if func_bobbing is Not its start position
2373 		VectorSubtract(bob_origin, bob_start, dir);
2374 		if (VectorLength(dir) > 16)
2375 		{
2376 #ifdef DEBUG_FUNCBOB
2377 			botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n");
2378 #endif
2379 			dist = dist1;
2380 			VectorCopy(dir1, dir);
2381 			//
2382 			BotCheckBlocked(ms, dir, qfalse, &result);
2383 			//
2384 			if (dist > 60) dist = 60;
2385 			speed = 360 - (360 - 6 * dist);
2386 			//
2387 			if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
2388 			{
2389 				if (speed > 5) EA_Move(ms->client, dir, speed);
2390 			} //end if
2391 			VectorCopy(dir, result.movedir);
2392 			//
2393 			if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2394 			//this isn't a failure... just wait till the func_bobbing arrives
2395 			result.type = RESULTTYPE_WAITFORFUNCBOBBING;
2396 			result.flags |= MOVERESULT_WAITING;
2397 			return result;
2398 		} //end if
2399 		//get direction and distance to func_bob bottom center
2400 		MoverBottomCenter(reach, bottomcenter);
2401 		VectorSubtract(bottomcenter, ms->origin, dir2);
2402 		if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
2403 		dist2 = VectorNormalize(dir2);
2404 		//if very close to the reachability start or
2405 		//closer to the elevator center or
2406 		//between reachability start and func_bobbing center
2407 		if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
2408 		{
2409 #ifdef DEBUG_FUNCBOB
2410 			botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
2411 #endif
2412 			dist = dist2;
2413 			VectorCopy(dir2, dir);
2414 		} //end if
2415 		else //closer to the reachability start
2416 		{
2417 #ifdef DEBUG_FUNCBOB
2418 			botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n");
2419 #endif
2420 			dist = dist1;
2421 			VectorCopy(dir1, dir);
2422 		} //end else
2423 		//
2424 		BotCheckBlocked(ms, dir, qfalse, &result);
2425 		//
2426 		if (dist > 60) dist = 60;
2427 		speed = 400 - (400 - 6 * dist);
2428 		//
2429 		if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
2430 		{
2431 			EA_Move(ms->client, dir, speed);
2432 		} //end if
2433 		VectorCopy(dir, result.movedir);
2434 		//
2435 		if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2436 	} //end else
2437 	return result;
2438 } //end of the function BotTravel_FuncBobbing
2439 //===========================================================================
2440 //
2441 // Parameter:			-
2442 // Returns:				-
2443 // Changes Globals:		-
2444 //===========================================================================
BotFinishTravel_FuncBobbing(bot_movestate_t * ms,aas_reachability_t * reach)2445 bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
2446 {
2447 	vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter;
2448 	bot_moveresult_t_cleared( result );
2449 	float dist, speed;
2450 
2451 	//
2452 	BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
2453 	//
2454 	VectorSubtract(bob_origin, bob_end, dir);
2455 	dist = VectorLength(dir);
2456 	//if the func_bobbing is near the end
2457 	if (dist < 16)
2458 	{
2459 		VectorSubtract(reach->end, ms->origin, hordir);
2460 		if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
2461 		dist = VectorNormalize(hordir);
2462 		//
2463 		if (dist > 60) dist = 60;
2464 		speed = 360 - (360 - 6 * dist);
2465 		//
2466 		if (speed > 5) EA_Move(ms->client, dir, speed);
2467 		VectorCopy(dir, result.movedir);
2468 		//
2469 		if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
2470 	} //end if
2471 	else
2472 	{
2473 		MoverBottomCenter(reach, bottomcenter);
2474 		VectorSubtract(bottomcenter, ms->origin, hordir);
2475 		if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
2476 		dist = VectorNormalize(hordir);
2477 		//
2478 		if (dist > 5)
2479 		{
2480 			//move to the center of the plat
2481 			if (dist > 100) dist = 100;
2482 			speed = 400 - (400 - 4 * dist);
2483 			//
2484 			EA_Move(ms->client, hordir, speed);
2485 			VectorCopy(hordir, result.movedir);
2486 		} //end if
2487 	} //end else
2488 	return result;
2489 } //end of the function BotFinishTravel_FuncBobbing
2490 //===========================================================================
2491 // 0  no valid grapple hook visible
2492 // 1  the grapple hook is still flying
2493 // 2  the grapple hooked into a wall
2494 //
2495 // Parameter:				-
2496 // Returns:					-
2497 // Changes Globals:		-
2498 //===========================================================================
GrappleState(bot_movestate_t * ms,aas_reachability_t * reach)2499 int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach)
2500 {
2501 	int i;
2502 	aas_entityinfo_t entinfo;
2503 
2504 	//if the grapple hook is pulling
2505 	if (ms->moveflags & MFL_GRAPPLEPULL)
2506 		return 2;
2507 	//check for a visible grapple missile entity
2508 	//or visible grapple entity
2509 	for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i))
2510 	{
2511 		if (AAS_EntityType(i) == (int) entitytypemissile->value)
2512 		{
2513 			AAS_EntityInfo(i, &entinfo);
2514 			if (entinfo.weapon == (int) weapindex_grapple->value)
2515 			{
2516 				return 1;
2517 			} //end if
2518 		} //end if
2519 	} //end for
2520 	//no valid grapple at all
2521 	return 0;
2522 } //end of the function GrappleState
2523 //===========================================================================
2524 //
2525 // Parameter:				-
2526 // Returns:					-
2527 // Changes Globals:		-
2528 //===========================================================================
BotResetGrapple(bot_movestate_t * ms)2529 void BotResetGrapple(bot_movestate_t *ms)
2530 {
2531 	aas_reachability_t reach;
2532 
2533 	AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
2534 	//if not using the grapple hook reachability anymore
2535 	if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK)
2536 	{
2537 		if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time)
2538 		{
2539 			if (offhandgrapple->value)
2540 				EA_Command(ms->client, cmd_grappleoff->string);
2541 			ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
2542 			ms->grapplevisible_time = 0;
2543 #ifdef DEBUG_GRAPPLE
2544 			botimport.Print(PRT_MESSAGE, "reset grapple\n");
2545 #endif //DEBUG_GRAPPLE
2546 		} //end if
2547 	} //end if
2548 } //end of the function BotResetGrapple
2549 //===========================================================================
2550 //
2551 // Parameter:			-
2552 // Returns:				-
2553 // Changes Globals:		-
2554 //===========================================================================
BotTravel_Grapple(bot_movestate_t * ms,aas_reachability_t * reach)2555 bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach)
2556 {
2557 	bot_moveresult_t_cleared( result );
2558 	float dist, speed;
2559 	vec3_t dir, viewdir, org;
2560 	int state, areanum;
2561 	bsp_trace_t trace;
2562 
2563 #ifdef DEBUG_GRAPPLE
2564 	static int debugline;
2565 	if (!debugline) debugline = botimport.DebugLineCreate();
2566 	botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE);
2567 #endif //DEBUG_GRAPPLE
2568 
2569 	//
2570 	if (ms->moveflags & MFL_GRAPPLERESET)
2571 	{
2572 		if (offhandgrapple->value)
2573 			EA_Command(ms->client, cmd_grappleoff->string);
2574 		ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
2575 		return result;
2576 	} //end if
2577 	//
2578 	if (!(int) offhandgrapple->value)
2579 	{
2580 		result.weapon = weapindex_grapple->value;
2581 		result.flags |= MOVERESULT_MOVEMENTWEAPON;
2582 	} //end if
2583 	//
2584 	if (ms->moveflags & MFL_ACTIVEGRAPPLE)
2585 	{
2586 #ifdef DEBUG_GRAPPLE
2587 		botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n");
2588 #endif //DEBUG_GRAPPLE
2589 		//
2590 		state = GrappleState(ms, reach);
2591 		//
2592 		VectorSubtract(reach->end, ms->origin, dir);
2593 		dir[2] = 0;
2594 		dist = VectorLength(dir);
2595 		//if very close to the grapple end or the grappled is hooked and
2596 		//the bot doesn't get any closer
2597 		if (state && dist < 48)
2598 		{
2599 			if (ms->lastgrappledist - dist < 1)
2600 			{
2601 #ifdef DEBUG_GRAPPLE
2602 				botimport.Print(PRT_ERROR, "grapple normal end\n");
2603 #endif //DEBUG_GRAPPLE
2604 				if (offhandgrapple->value)
2605 					EA_Command(ms->client, cmd_grappleoff->string);
2606 				ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
2607 				ms->moveflags |= MFL_GRAPPLERESET;
2608 				ms->reachability_time = 0;	//end the reachability
2609 				return result;
2610 			} //end if
2611 		} //end if
2612 		//if no valid grapple at all, or the grapple hooked and the bot
2613 		//isn't moving anymore
2614 		else if (!state || (state == 2 && dist > ms->lastgrappledist - 2))
2615 		{
2616 			if (ms->grapplevisible_time < AAS_Time() - 0.4)
2617 			{
2618 #ifdef DEBUG_GRAPPLE
2619 				botimport.Print(PRT_ERROR, "grapple not visible\n");
2620 #endif //DEBUG_GRAPPLE
2621 				if (offhandgrapple->value)
2622 					EA_Command(ms->client, cmd_grappleoff->string);
2623 				ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
2624 				ms->moveflags |= MFL_GRAPPLERESET;
2625 				ms->reachability_time = 0;	//end the reachability
2626 				return result;
2627 			} //end if
2628 		} //end if
2629 		else
2630 		{
2631 			ms->grapplevisible_time = AAS_Time();
2632 		} //end else
2633 		//
2634 		if (!(int) offhandgrapple->value)
2635 		{
2636 			EA_Attack(ms->client);
2637 		} //end if
2638 		//remember the current grapple distance
2639 		ms->lastgrappledist = dist;
2640 	} //end if
2641 	else
2642 	{
2643 #ifdef DEBUG_GRAPPLE
2644 		botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n");
2645 #endif //DEBUG_GRAPPLE
2646 		//
2647 		ms->grapplevisible_time = AAS_Time();
2648 		//
2649 		VectorSubtract(reach->start, ms->origin, dir);
2650 		if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0;
2651 		VectorAdd(ms->origin, ms->viewoffset, org);
2652 		VectorSubtract(reach->end, org, viewdir);
2653 		//
2654 		dist = VectorNormalize(dir);
2655 		Vector2Angles(viewdir, result.ideal_viewangles);
2656 		result.flags |= MOVERESULT_MOVEMENTVIEW;
2657 		//
2658 		if (dist < 5 &&
2659 			fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 &&
2660 			fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2)
2661 		{
2662 #ifdef DEBUG_GRAPPLE
2663 			botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n");
2664 #endif //DEBUG_GRAPPLE
2665 			//check if the grapple missile path is clear
2666 			VectorAdd(ms->origin, ms->viewoffset, org);
2667 			trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID);
2668 			VectorSubtract(reach->end, trace.endpos, dir);
2669 			if (VectorLength(dir) > 16)
2670 			{
2671 				result.failure = qtrue;
2672 				return result;
2673 			} //end if
2674 			//activate the grapple
2675 			if (offhandgrapple->value)
2676 			{
2677 				EA_Command(ms->client, cmd_grappleon->string);
2678 			} //end if
2679 			else
2680 			{
2681 				EA_Attack(ms->client);
2682 			} //end else
2683 			ms->moveflags |= MFL_ACTIVEGRAPPLE;
2684 			ms->lastgrappledist = 999999;
2685 		} //end if
2686 		else
2687 		{
2688 			if (dist < 70) speed = 300 - (300 - 4 * dist);
2689 			else speed = 400;
2690 			//
2691 			BotCheckBlocked(ms, dir, qtrue, &result);
2692 			//elemantary action move in direction
2693 			EA_Move(ms->client, dir, speed);
2694 			VectorCopy(dir, result.movedir);
2695 		} //end else
2696 		//if in another area before actually grappling
2697 		areanum = AAS_PointAreaNum(ms->origin);
2698 		if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0;
2699 	} //end else
2700 	return result;
2701 } //end of the function BotTravel_Grapple
2702 //===========================================================================
2703 //
2704 // Parameter:				-
2705 // Returns:					-
2706 // Changes Globals:			-
2707 //===========================================================================
BotTravel_RocketJump(bot_movestate_t * ms,aas_reachability_t * reach)2708 bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach)
2709 {
2710 	vec3_t hordir;
2711 	float dist, speed;
2712 	bot_moveresult_t_cleared( result );
2713 
2714 	//botimport.Print(PRT_MESSAGE, "BotTravel_RocketJump: bah\n");
2715 	//
2716 	hordir[0] = reach->start[0] - ms->origin[0];
2717 	hordir[1] = reach->start[1] - ms->origin[1];
2718 	hordir[2] = 0;
2719 	//
2720 	dist = VectorNormalize(hordir);
2721 	//look in the movement direction
2722 	Vector2Angles(hordir, result.ideal_viewangles);
2723 	//look straight down
2724 	result.ideal_viewangles[PITCH] = 90;
2725 	//
2726 	if (dist < 5 &&
2727 			fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
2728 			fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
2729 	{
2730 		//botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
2731 		hordir[0] = reach->end[0] - ms->origin[0];
2732 		hordir[1] = reach->end[1] - ms->origin[1];
2733 		hordir[2] = 0;
2734 		VectorNormalize(hordir);
2735 		//elemantary action jump
2736 		EA_Jump(ms->client);
2737 		EA_Attack(ms->client);
2738 		EA_Move(ms->client, hordir, 800);
2739 		//
2740 		ms->jumpreach = ms->lastreachnum;
2741 	} //end if
2742 	else
2743 	{
2744 		if (dist > 80) dist = 80;
2745 		speed = 400 - (400 - 5 * dist);
2746 		EA_Move(ms->client, hordir, speed);
2747 	} //end else
2748 	//look in the movement direction
2749 	Vector2Angles(hordir, result.ideal_viewangles);
2750 	//look straight down
2751 	result.ideal_viewangles[PITCH] = 90;
2752 	//set the view angles directly
2753 	EA_View(ms->client, result.ideal_viewangles);
2754 	//view is important for the movment
2755 	result.flags |= MOVERESULT_MOVEMENTVIEWSET;
2756 	//select the rocket launcher
2757 	EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value);
2758 	//weapon is used for movement
2759 	result.weapon = (int) weapindex_rocketlauncher->value;
2760 	result.flags |= MOVERESULT_MOVEMENTWEAPON;
2761 	//
2762 	VectorCopy(hordir, result.movedir);
2763 	//
2764 	return result;
2765 } //end of the function BotTravel_RocketJump
2766 //===========================================================================
2767 //
2768 // Parameter:				-
2769 // Returns:					-
2770 // Changes Globals:		-
2771 //===========================================================================
BotTravel_BFGJump(bot_movestate_t * ms,aas_reachability_t * reach)2772 bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach)
2773 {
2774 	vec3_t hordir;
2775 	float dist, speed;
2776 	bot_moveresult_t_cleared( result );
2777 
2778 	//botimport.Print(PRT_MESSAGE, "BotTravel_BFGJump: bah\n");
2779 	//
2780 	hordir[0] = reach->start[0] - ms->origin[0];
2781 	hordir[1] = reach->start[1] - ms->origin[1];
2782 	hordir[2] = 0;
2783 	//
2784 	dist = VectorNormalize(hordir);
2785 	//
2786 	if (dist < 5 &&
2787 			fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
2788 			fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
2789 	{
2790 		//botimport.Print(PRT_MESSAGE, "between jump start and run start point\n");
2791 		hordir[0] = reach->end[0] - ms->origin[0];
2792 		hordir[1] = reach->end[1] - ms->origin[1];
2793 		hordir[2] = 0;
2794 		VectorNormalize(hordir);
2795 		//elemantary action jump
2796 		EA_Jump(ms->client);
2797 		EA_Attack(ms->client);
2798 		EA_Move(ms->client, hordir, 800);
2799 		//
2800 		ms->jumpreach = ms->lastreachnum;
2801 	} //end if
2802 	else
2803 	{
2804 		if (dist > 80) dist = 80;
2805 		speed = 400 - (400 - 5 * dist);
2806 		EA_Move(ms->client, hordir, speed);
2807 	} //end else
2808 	//look in the movement direction
2809 	Vector2Angles(hordir, result.ideal_viewangles);
2810 	//look straight down
2811 	result.ideal_viewangles[PITCH] = 90;
2812 	//set the view angles directly
2813 	EA_View(ms->client, result.ideal_viewangles);
2814 	//view is important for the movment
2815 	result.flags |= MOVERESULT_MOVEMENTVIEWSET;
2816 	//select the rocket launcher
2817 	EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value);
2818 	//weapon is used for movement
2819 	result.weapon = (int) weapindex_bfg10k->value;
2820 	result.flags |= MOVERESULT_MOVEMENTWEAPON;
2821 	//
2822 	VectorCopy(hordir, result.movedir);
2823 	//
2824 	return result;
2825 } //end of the function BotTravel_BFGJump
2826 //===========================================================================
2827 //
2828 // Parameter:				-
2829 // Returns:					-
2830 // Changes Globals:		-
2831 //===========================================================================
BotFinishTravel_WeaponJump(bot_movestate_t * ms,aas_reachability_t * reach)2832 bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach)
2833 {
2834 	vec3_t hordir;
2835 	float speed;
2836 	bot_moveresult_t_cleared( result );
2837 
2838 	//if not jumped yet
2839 	if (!ms->jumpreach) return result;
2840 	/*
2841 	//go straight to the reachability end
2842 	hordir[0] = reach->end[0] - ms->origin[0];
2843 	hordir[1] = reach->end[1] - ms->origin[1];
2844 	hordir[2] = 0;
2845 	VectorNormalize(hordir);
2846 	//always use max speed when traveling through the air
2847 	EA_Move(ms->client, hordir, 800);
2848 	VectorCopy(hordir, result.movedir);
2849 	*/
2850 	//
2851 	if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
2852 	{
2853 		//go straight to the reachability end
2854 		VectorSubtract(reach->end, ms->origin, hordir);
2855 		hordir[2] = 0;
2856 		VectorNormalize(hordir);
2857 		speed = 400;
2858 	} //end if
2859 	//
2860 	EA_Move(ms->client, hordir, speed);
2861 	VectorCopy(hordir, result.movedir);
2862 	//
2863 	return result;
2864 } //end of the function BotFinishTravel_WeaponJump
2865 //===========================================================================
2866 //
2867 // Parameter:				-
2868 // Returns:					-
2869 // Changes Globals:		-
2870 //===========================================================================
BotTravel_JumpPad(bot_movestate_t * ms,aas_reachability_t * reach)2871 bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
2872 {
2873 	vec3_t hordir;
2874 	bot_moveresult_t_cleared( result );
2875 
2876 	//first walk straight to the reachability start
2877 	hordir[0] = reach->start[0] - ms->origin[0];
2878 	hordir[1] = reach->start[1] - ms->origin[1];
2879 	hordir[2] = 0;
2880 	//
2881 	BotCheckBlocked(ms, hordir, qtrue, &result);
2882 	//elemantary action move in direction
2883 	EA_Move(ms->client, hordir, 400);
2884 	VectorCopy(hordir, result.movedir);
2885 	//
2886 	return result;
2887 } //end of the function BotTravel_JumpPad
2888 //===========================================================================
2889 //
2890 // Parameter:			-
2891 // Returns:				-
2892 // Changes Globals:		-
2893 //===========================================================================
BotFinishTravel_JumpPad(bot_movestate_t * ms,aas_reachability_t * reach)2894 bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
2895 {
2896 	float speed;
2897 	vec3_t hordir;
2898 	bot_moveresult_t_cleared( result );
2899 
2900 	if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
2901 	{
2902 		hordir[0] = reach->end[0] - ms->origin[0];
2903 		hordir[1] = reach->end[1] - ms->origin[1];
2904 		hordir[2] = 0;
2905 		VectorNormalize(hordir);
2906 		speed = 400;
2907 	} //end if
2908 	BotCheckBlocked(ms, hordir, qtrue, &result);
2909 	//elemantary action move in direction
2910 	EA_Move(ms->client, hordir, speed);
2911 	VectorCopy(hordir, result.movedir);
2912 	//
2913 	return result;
2914 } //end of the function BotFinishTravel_JumpPad
2915 //===========================================================================
2916 // time before the reachability times out
2917 //
2918 // Parameter:				-
2919 // Returns:					-
2920 // Changes Globals:		-
2921 //===========================================================================
BotReachabilityTime(aas_reachability_t * reach)2922 int BotReachabilityTime(aas_reachability_t *reach)
2923 {
2924 	switch(reach->traveltype & TRAVELTYPE_MASK)
2925 	{
2926 		case TRAVEL_WALK: return 5;
2927 		case TRAVEL_CROUCH: return 5;
2928 		case TRAVEL_BARRIERJUMP: return 5;
2929 		case TRAVEL_LADDER: return 6;
2930 		case TRAVEL_WALKOFFLEDGE: return 5;
2931 		case TRAVEL_JUMP: return 5;
2932 		case TRAVEL_SWIM: return 5;
2933 		case TRAVEL_WATERJUMP: return 5;
2934 		case TRAVEL_TELEPORT: return 5;
2935 		case TRAVEL_ELEVATOR: return 10;
2936 		case TRAVEL_GRAPPLEHOOK: return 8;
2937 		case TRAVEL_ROCKETJUMP: return 6;
2938 		case TRAVEL_BFGJUMP: return 6;
2939 		case TRAVEL_JUMPPAD: return 10;
2940 		case TRAVEL_FUNCBOB: return 10;
2941 		default:
2942 		{
2943 			botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype);
2944 			return 8;
2945 		} //end case
2946 	} //end switch
2947 } //end of the function BotReachabilityTime
2948 //===========================================================================
2949 //
2950 // Parameter:				-
2951 // Returns:					-
2952 // Changes Globals:		-
2953 //===========================================================================
BotMoveInGoalArea(bot_movestate_t * ms,bot_goal_t * goal)2954 bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal)
2955 {
2956 	bot_moveresult_t_cleared( result );
2957 	vec3_t dir;
2958 	float dist, speed;
2959 
2960 #ifdef DEBUG
2961 	//botimport.Print(PRT_MESSAGE, "%s: moving straight to goal\n", ClientName(ms->entitynum-1));
2962 	//AAS_ClearShownDebugLines();
2963 	//AAS_DebugLine(ms->origin, goal->origin, LINECOLOR_RED);
2964 #endif //DEBUG
2965 	//walk straight to the goal origin
2966 	dir[0] = goal->origin[0] - ms->origin[0];
2967 	dir[1] = goal->origin[1] - ms->origin[1];
2968 	if (ms->moveflags & MFL_SWIMMING)
2969 	{
2970 		dir[2] = goal->origin[2] - ms->origin[2];
2971 		result.traveltype = TRAVEL_SWIM;
2972 	} //end if
2973 	else
2974 	{
2975 		dir[2] = 0;
2976 		result.traveltype = TRAVEL_WALK;
2977 	} //endif
2978 	//
2979 	dist = VectorNormalize(dir);
2980 	if (dist > 100) dist = 100;
2981 	speed = 400 - (400 - 4 * dist);
2982 	if (speed < 10) speed = 0;
2983 	//
2984 	BotCheckBlocked(ms, dir, qtrue, &result);
2985 	//elemantary action move in direction
2986 	EA_Move(ms->client, dir, speed);
2987 	VectorCopy(dir, result.movedir);
2988 	//
2989 	if (ms->moveflags & MFL_SWIMMING)
2990 	{
2991 		Vector2Angles(dir, result.ideal_viewangles);
2992 		result.flags |= MOVERESULT_SWIMVIEW;
2993 	} //end if
2994 	//if (!debugline) debugline = botimport.DebugLineCreate();
2995 	//botimport.DebugLineShow(debugline, ms->origin, goal->origin, LINECOLOR_BLUE);
2996 	//
2997 	ms->lastreachnum = 0;
2998 	ms->lastareanum = 0;
2999 	ms->lastgoalareanum = goal->areanum;
3000 	VectorCopy(ms->origin, ms->lastorigin);
3001 	//
3002 	return result;
3003 } //end of the function BotMoveInGoalArea
3004 //===========================================================================
3005 //
3006 // Parameter:				-
3007 // Returns:					-
3008 // Changes Globals:		-
3009 //===========================================================================
BotMoveToGoal(bot_moveresult_t * result,int movestate,bot_goal_t * goal,int travelflags)3010 void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags)
3011 {
3012 	int reachnum, lastreachnum, foundjumppad, ent, resultflags;
3013 	aas_reachability_t reach, lastreach;
3014 	bot_movestate_t *ms;
3015 	//vec3_t mins, maxs, up = {0, 0, 1};
3016 	//bsp_trace_t trace;
3017 	//static int debugline;
3018 
3019 	result->failure = qfalse;
3020 	result->type = 0;
3021 	result->blocked = qfalse;
3022 	result->blockentity = 0;
3023 	result->traveltype = 0;
3024 	result->flags = 0;
3025 
3026 	//
3027 	ms = BotMoveStateFromHandle(movestate);
3028 	if (!ms) return;
3029 	//reset the grapple before testing if the bot has a valid goal
3030 	//because the bot could lose all its goals when stuck to a wall
3031 	BotResetGrapple(ms);
3032 	//
3033 	if (!goal)
3034 	{
3035 #ifdef DEBUG
3036 		botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client);
3037 #endif //DEBUG
3038 		result->failure = qtrue;
3039 		return;
3040 	} //end if
3041 	//botimport.Print(PRT_MESSAGE, "numavoidreach = %d\n", ms->numavoidreach);
3042 	//remove some of the move flags
3043 	ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER);
3044 	//set some of the move flags
3045 	//NOTE: the MFL_ONGROUND flag is also set in the higher AI
3046 	if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
3047 	//
3048 	if (ms->moveflags & MFL_ONGROUND)
3049 	{
3050 		int modeltype, modelnum;
3051 
3052 		ent = BotOnTopOfEntity(ms);
3053 
3054 		if (ent != -1)
3055 		{
3056 			modelnum = AAS_EntityModelindex(ent);
3057 			if (modelnum >= 0 && modelnum < MAX_MODELS)
3058 			{
3059 				modeltype = modeltypes[modelnum];
3060 
3061 				if (modeltype == MODELTYPE_FUNC_PLAT)
3062 				{
3063 					AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
3064 					//if the bot is Not using the elevator
3065 					if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR ||
3066 						//NOTE: the face number is the plat model number
3067 						(reach.facenum & 0x0000FFFF) != modelnum)
3068 					{
3069 						reachnum = AAS_NextModelReachability(0, modelnum);
3070 						if (reachnum)
3071 						{
3072 							//botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_plat\n", ms->client);
3073 							AAS_ReachabilityFromNum(reachnum, &reach);
3074 							ms->lastreachnum = reachnum;
3075 							ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
3076 						} //end if
3077 						else
3078 						{
3079 							if (botDeveloper)
3080 							{
3081 								botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client);
3082 							} //end if
3083 							result->blocked = qtrue;
3084 							result->blockentity = ent;
3085 							result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
3086 							return;
3087 						} //end else
3088 					} //end if
3089 					result->flags |= MOVERESULT_ONTOPOF_ELEVATOR;
3090 				} //end if
3091 				else if (modeltype == MODELTYPE_FUNC_BOB)
3092 				{
3093 					AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
3094 					//if the bot is Not using the func bobbing
3095 					if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB ||
3096 						//NOTE: the face number is the func_bobbing model number
3097 						(reach.facenum & 0x0000FFFF) != modelnum)
3098 					{
3099 						reachnum = AAS_NextModelReachability(0, modelnum);
3100 						if (reachnum)
3101 						{
3102 							//botimport.Print(PRT_MESSAGE, "client %d: accidentally ended up on func_bobbing\n", ms->client);
3103 							AAS_ReachabilityFromNum(reachnum, &reach);
3104 							ms->lastreachnum = reachnum;
3105 							ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
3106 						} //end if
3107 						else
3108 						{
3109 							if (botDeveloper)
3110 							{
3111 								botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client);
3112 							} //end if
3113 							result->blocked = qtrue;
3114 							result->blockentity = ent;
3115 							result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
3116 							return;
3117 						} //end else
3118 					} //end if
3119 					result->flags |= MOVERESULT_ONTOPOF_FUNCBOB;
3120 				} //end if
3121 				else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR)
3122 				{
3123 					// check if ontop of a door bridge ?
3124 					ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
3125 					// if not in a reachability area
3126 					if (!AAS_AreaReachability(ms->areanum))
3127 					{
3128 						result->blocked = qtrue;
3129 						result->blockentity = ent;
3130 						result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
3131 						return;
3132 					} //end if
3133 				} //end else if
3134 				else
3135 				{
3136 					result->blocked = qtrue;
3137 					result->blockentity = ent;
3138 					result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
3139 					return;
3140 				} //end else
3141 			} //end if
3142 		} //end if
3143 	} //end if
3144 	//if swimming
3145 	if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING;
3146 	//if against a ladder
3147 	if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER;
3148 	//if the bot is on the ground, swimming or against a ladder
3149 	if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER))
3150 	{
3151 		//botimport.Print(PRT_MESSAGE, "%s: onground, swimming or against ladder\n", ClientName(ms->entitynum-1));
3152 		//
3153 		AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach);
3154 		//reachability area the bot is in
3155 		//ms->areanum = BotReachabilityArea(ms->origin, ((lastreach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR));
3156 		ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
3157 		//
3158 		if ( !ms->areanum )
3159 		{
3160 			result->failure = qtrue;
3161 			result->blocked = qtrue;
3162 			result->blockentity = 0;
3163 			result->type = RESULTTYPE_INSOLIDAREA;
3164 			return;
3165 		} //end if
3166 		//if the bot is in the goal area
3167 		if (ms->areanum == goal->areanum)
3168 		{
3169 			*result = BotMoveInGoalArea(ms, goal);
3170 			return;
3171 		} //end if
3172 		//assume we can use the reachability from the last frame
3173 		reachnum = ms->lastreachnum;
3174 		//if there is a last reachability
3175 		if (reachnum)
3176 		{
3177 			AAS_ReachabilityFromNum(reachnum, &reach);
3178 			//check if the reachability is still valid
3179 			if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags))
3180 			{
3181 				reachnum = 0;
3182 			} //end if
3183 			//special grapple hook case
3184 			else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK)
3185 			{
3186 				if (ms->reachability_time < AAS_Time() ||
3187 					(ms->moveflags & MFL_GRAPPLERESET))
3188 				{
3189 					reachnum = 0;
3190 				} //end if
3191 			} //end if
3192 			//special elevator case
3193 			else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR ||
3194 				(reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
3195 			{
3196 				if ((result->flags & MOVERESULT_ONTOPOF_ELEVATOR) ||
3197 					(result->flags & MOVERESULT_ONTOPOF_FUNCBOB))
3198 				{
3199 					ms->reachability_time = AAS_Time() + 5;
3200 				} //end if
3201 				//if the bot was going for an elevator and reached the reachability area
3202 				if (ms->areanum == reach.areanum ||
3203 					ms->reachability_time < AAS_Time())
3204 				{
3205 					reachnum = 0;
3206 				} //end if
3207 			} //end if
3208 			else
3209 			{
3210 #ifdef DEBUG
3211 				if (botDeveloper)
3212 				{
3213 					if (ms->reachability_time < AAS_Time())
3214 					{
3215 						botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client);
3216 						AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
3217 						botimport.Print(PRT_MESSAGE, "\n");
3218 					} //end if
3219 					/*
3220 					if (ms->lastareanum != ms->areanum)
3221 					{
3222 						botimport.Print(PRT_MESSAGE, "changed from area %d to %d\n", ms->lastareanum, ms->areanum);
3223 					} //end if*/
3224 				} //end if
3225 #endif //DEBUG
3226 				//if the goal area changed or the reachability timed out
3227 				//or the area changed
3228 				if (ms->lastgoalareanum != goal->areanum ||
3229 						ms->reachability_time < AAS_Time() ||
3230 						ms->lastareanum != ms->areanum)
3231 				{
3232 					reachnum = 0;
3233 					//botimport.Print(PRT_MESSAGE, "area change or timeout\n");
3234 				} //end else if
3235 			} //end else
3236 		} //end if
3237 		resultflags = 0;
3238 		//if the bot needs a new reachability
3239 		if (!reachnum)
3240 		{
3241 			//if the area has no reachability links
3242 			if (!AAS_AreaReachability(ms->areanum))
3243 			{
3244 #ifdef DEBUG
3245 				if (botDeveloper)
3246 				{
3247 					botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum);
3248 				} //end if
3249 #endif //DEBUG
3250 			} //end if
3251 			//get a new reachability leading towards the goal
3252 			reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum,
3253 								ms->lastgoalareanum, ms->lastareanum,
3254 											ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
3255 														goal, travelflags,
3256 																ms->avoidspots, ms->numavoidspots, &resultflags);
3257 			//the area number the reachability starts in
3258 			ms->reachareanum = ms->areanum;
3259 			//reset some state variables
3260 			ms->jumpreach = 0;						//for TRAVEL_JUMP
3261 			ms->moveflags &= ~MFL_GRAPPLERESET;	//for TRAVEL_GRAPPLEHOOK
3262 			//if there is a reachability to the goal
3263 			if (reachnum)
3264 			{
3265 				AAS_ReachabilityFromNum(reachnum, &reach);
3266 				//set a timeout for this reachability
3267 				ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
3268 				//
3269 #ifdef AVOIDREACH
3270 				//add the reachability to the reachabilities to avoid for a while
3271 				BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME);
3272 #endif //AVOIDREACH
3273 			} //end if
3274 #ifdef DEBUG
3275 
3276 			else if (botDeveloper)
3277 			{
3278 				botimport.Print(PRT_MESSAGE, "goal not reachable\n");
3279 				Com_Memset(&reach, 0, sizeof(aas_reachability_t)); //make compiler happy
3280 			} //end else
3281 			if (botDeveloper)
3282 			{
3283 				//if still going for the same goal
3284 				if (ms->lastgoalareanum == goal->areanum)
3285 				{
3286 					if (ms->lastareanum == reach.areanum)
3287 					{
3288 						botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n");
3289 					} //end if
3290 				} //end if
3291 			} //end if
3292 #endif //DEBUG
3293 		} //end else
3294 		//
3295 		ms->lastreachnum = reachnum;
3296 		ms->lastgoalareanum = goal->areanum;
3297 		ms->lastareanum = ms->areanum;
3298 		//if the bot has a reachability
3299 		if (reachnum)
3300 		{
3301 			//get the reachability from the number
3302 			AAS_ReachabilityFromNum(reachnum, &reach);
3303 			result->traveltype = reach.traveltype;
3304 			//
3305 #ifdef DEBUG_AI_MOVE
3306 			AAS_ClearShownDebugLines();
3307 			AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
3308 			AAS_ShowReachability(&reach);
3309 #endif //DEBUG_AI_MOVE
3310 			//
3311 #ifdef DEBUG
3312 			//botimport.Print(PRT_MESSAGE, "client %d: ", ms->client);
3313 			//AAS_PrintTravelType(reach.traveltype);
3314 			//botimport.Print(PRT_MESSAGE, "\n");
3315 #endif //DEBUG
3316 			switch(reach.traveltype & TRAVELTYPE_MASK)
3317 			{
3318 				case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;
3319 				case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break;
3320 				case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break;
3321 				case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
3322 				case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break;
3323 				case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break;
3324 				case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
3325 				case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break;
3326 				case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break;
3327 				case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break;
3328 				case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
3329 				case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break;
3330 				case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break;
3331 				case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break;
3332 				case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break;
3333 				default:
3334 				{
3335 					botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
3336 					break;
3337 				} //end case
3338 			} //end switch
3339 			result->traveltype = reach.traveltype;
3340 			result->flags |= resultflags;
3341 		} //end if
3342 		else
3343 		{
3344 			result->failure = qtrue;
3345 			result->flags |= resultflags;
3346 			Com_Memset(&reach, 0, sizeof(aas_reachability_t));
3347 		} //end else
3348 #ifdef DEBUG
3349 		if (botDeveloper)
3350 		{
3351 			if (result->failure)
3352 			{
3353 				botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client);
3354 				AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
3355 				botimport.Print(PRT_MESSAGE, "\n");
3356 			} //end if
3357 		} //end if
3358 #endif //DEBUG
3359 	} //end if
3360 	else
3361 	{
3362 		int i, numareas, areas[16];
3363 		vec3_t end;
3364 
3365 		//special handling of jump pads when the bot uses a jump pad without knowing it
3366 		foundjumppad = qfalse;
3367 		VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end);
3368 		numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16);
3369 		for (i = numareas-1; i >= 0; i--)
3370 		{
3371 			if (AAS_AreaJumpPad(areas[i]))
3372 			{
3373 				//botimport.Print(PRT_MESSAGE, "client %d used a jumppad without knowing, area %d\n", ms->client, areas[i]);
3374 				foundjumppad = qtrue;
3375 				lastreachnum = BotGetReachabilityToGoal(end, areas[i],
3376 							ms->lastgoalareanum, ms->lastareanum,
3377 							ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
3378 							goal, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL);
3379 				if (lastreachnum)
3380 				{
3381 					ms->lastreachnum = lastreachnum;
3382 					ms->lastareanum = areas[i];
3383 					//botimport.Print(PRT_MESSAGE, "found jumppad reachability\n");
3384 					break;
3385 				} //end if
3386 				else
3387 				{
3388 					for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum;
3389 						lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum))
3390 					{
3391 						//get the reachability from the number
3392 						AAS_ReachabilityFromNum(lastreachnum, &reach);
3393 						if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)
3394 						{
3395 							ms->lastreachnum = lastreachnum;
3396 							ms->lastareanum = areas[i];
3397 							//botimport.Print(PRT_MESSAGE, "found jumppad reachability hard!!\n");
3398 						} //end if
3399 					} //end for
3400 					if (lastreachnum) break;
3401 				} //end else
3402 			} //end if
3403 		} //end for
3404 		if (botDeveloper)
3405 		{
3406 			//if a jumppad is found with the trace but no reachability is found
3407 			if (foundjumppad && !ms->lastreachnum)
3408 			{
3409 				botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client);
3410 			} //end if
3411 		} //end if
3412 		//
3413 		if (ms->lastreachnum)
3414 		{
3415 			//botimport.Print(PRT_MESSAGE, "%s: NOT onground, swimming or against ladder\n", ClientName(ms->entitynum-1));
3416 			AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
3417 			result->traveltype = reach.traveltype;
3418 #ifdef DEBUG
3419 			//botimport.Print(PRT_MESSAGE, "client %d finish: ", ms->client);
3420 			//AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
3421 			//botimport.Print(PRT_MESSAGE, "\n");
3422 #endif //DEBUG
3423 			//
3424 			switch(reach.traveltype & TRAVELTYPE_MASK)
3425 			{
3426 				case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;//BotFinishTravel_Walk(ms, &reach); break;
3427 				case TRAVEL_CROUCH: /*do nothing*/ break;
3428 				case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break;
3429 				case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
3430 				case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break;
3431 				case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break;
3432 				case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
3433 				case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break;
3434 				case TRAVEL_TELEPORT: /*do nothing*/ break;
3435 				case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break;
3436 				case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
3437 				case TRAVEL_ROCKETJUMP:
3438 				case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break;
3439 				case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break;
3440 				case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break;
3441 				default:
3442 				{
3443 					botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
3444 					break;
3445 				} //end case
3446 			} //end switch
3447 			result->traveltype = reach.traveltype;
3448 #ifdef DEBUG
3449 			if (botDeveloper)
3450 			{
3451 				if (result->failure)
3452 				{
3453 					botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client);
3454 					AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
3455 					botimport.Print(PRT_MESSAGE, "\n");
3456 				} //end if
3457 			} //end if
3458 #endif //DEBUG
3459 		} //end if
3460 	} //end else
3461 	//FIXME: is it right to do this here?
3462 	if (result->blocked) ms->reachability_time -= 10 * ms->thinktime;
3463 	//copy the last origin
3464 	VectorCopy(ms->origin, ms->lastorigin);
3465 	//return the movement result
3466 	return;
3467 } //end of the function BotMoveToGoal
3468 //===========================================================================
3469 //
3470 // Parameter:				-
3471 // Returns:					-
3472 // Changes Globals:		-
3473 //===========================================================================
BotResetAvoidReach(int movestate)3474 void BotResetAvoidReach(int movestate)
3475 {
3476 	bot_movestate_t *ms;
3477 
3478 	ms = BotMoveStateFromHandle(movestate);
3479 	if (!ms) return;
3480 	Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
3481 	Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float));
3482 	Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int));
3483 } //end of the function BotResetAvoidReach
3484 //===========================================================================
3485 //
3486 // Parameter:				-
3487 // Returns:					-
3488 // Changes Globals:		-
3489 //===========================================================================
BotResetLastAvoidReach(int movestate)3490 void BotResetLastAvoidReach(int movestate)
3491 {
3492 	int i, latest;
3493 	float latesttime;
3494 	bot_movestate_t *ms;
3495 
3496 	ms = BotMoveStateFromHandle(movestate);
3497 	if (!ms) return;
3498 	latesttime = 0;
3499 	latest = 0;
3500 	for (i = 0; i < MAX_AVOIDREACH; i++)
3501 	{
3502 		if (ms->avoidreachtimes[i] > latesttime)
3503 		{
3504 			latesttime = ms->avoidreachtimes[i];
3505 			latest = i;
3506 		} //end if
3507 	} //end for
3508 	if (latesttime)
3509 	{
3510 		ms->avoidreachtimes[latest] = 0;
3511 		if (ms->avoidreachtries[latest] > 0) ms->avoidreachtries[latest]--;
3512 	} //end if
3513 } //end of the function BotResetLastAvoidReach
3514 //===========================================================================
3515 //
3516 // Parameter:			-
3517 // Returns:				-
3518 // Changes Globals:		-
3519 //===========================================================================
BotResetMoveState(int movestate)3520 void BotResetMoveState(int movestate)
3521 {
3522 	bot_movestate_t *ms;
3523 
3524 	ms = BotMoveStateFromHandle(movestate);
3525 	if (!ms) return;
3526 	Com_Memset(ms, 0, sizeof(bot_movestate_t));
3527 } //end of the function BotResetMoveState
3528 //===========================================================================
3529 //
3530 // Parameter:			-
3531 // Returns:				-
3532 // Changes Globals:		-
3533 //===========================================================================
BotSetupMoveAI(void)3534 int BotSetupMoveAI(void)
3535 {
3536 	BotSetBrushModelTypes();
3537 	sv_maxstep = LibVar("sv_step", "18");
3538 	sv_maxbarrier = LibVar("sv_maxbarrier", "32");
3539 	sv_gravity = LibVar("sv_gravity", "800");
3540 	weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5");
3541 	weapindex_bfg10k = LibVar("weapindex_bfg10k", "9");
3542 	weapindex_grapple = LibVar("weapindex_grapple", "10");
3543 	entitytypemissile = LibVar("entitytypemissile", "3");
3544 	offhandgrapple = LibVar("offhandgrapple", "0");
3545 	cmd_grappleon = LibVar("cmd_grappleon", "grappleon");
3546 	cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff");
3547 	return BLERR_NOERROR;
3548 } //end of the function BotSetupMoveAI
3549 //===========================================================================
3550 //
3551 // Parameter:			-
3552 // Returns:				-
3553 // Changes Globals:		-
3554 //===========================================================================
BotShutdownMoveAI(void)3555 void BotShutdownMoveAI(void)
3556 {
3557 	int i;
3558 
3559 	for (i = 1; i <= MAX_CLIENTS; i++)
3560 	{
3561 		if (botmovestates[i])
3562 		{
3563 			FreeMemory(botmovestates[i]);
3564 			botmovestates[i] = NULL;
3565 		} //end if
3566 	} //end for
3567 } //end of the function BotShutdownMoveAI
3568 
3569 
3570