Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

be_ai_move.c (113070B)


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