1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 1998 Steve Yeager
4 Copyright (C) 2010 COR Entertainment, LLC.
5 
6 See below for Steve Yeager's original copyright notice.
7 Modified to GPL in 2002.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License along
21 with this program; if not, write to the Free Software Foundation, Inc.,
22 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24 ///////////////////////////////////////////////////////////////////////
25 //
26 //  ACE - Quake II Bot Base Code
27 //
28 //  Version 1.0
29 //
30 //  This file is Copyright(c), Steve Yeager 1998, All Rights Reserved
31 //
32 //
33 //	All other files are Copyright(c) Id Software, Inc.
34 //
35 //	Please see liscense.txt in the source directory for the copyright
36 //	information regarding those files belonging to Id Software, Inc.
37 //
38 //	Should you decide to release a modified version of ACE, you MUST
39 //	include the following text (minus the BEGIN and END lines) in the
40 //	documentation for your modification.
41 //
42 //	--- BEGIN ---
43 //
44 //	The ACE Bot is a product of Steve Yeager, and is available from
45 //	the ACE Bot homepage, at http://www.axionfx.com/ace.
46 //
47 //	This program is a modification of the ACE Bot, and is therefore
48 //	in NO WAY supported by Steve Yeager.
49 
50 //	This program MUST NOT be sold in ANY form. If you have paid for
51 //	this product, you should contact Steve Yeager immediately, via
52 //	the ACE Bot homepage.
53 //
54 //	--- END ---
55 //
56 //	I, Steve Yeager, hold no responsibility for any harm caused by the
57 //	use of this source code, especially to small children and animals.
58 //  It is provided as-is with no implied warranty or support.
59 //
60 //  I also wish to thank and acknowledge the great work of others
61 //  that has helped me to develop this code.
62 //
63 //  John Cricket    - For ideas and swapping code.
64 //  Ryan Feltrin    - For ideas and swapping code.
65 //  SABIN           - For showing how to do true client based movement.
66 //  BotEpidemic     - For keeping us up to date.
67 //  Telefragged.com - For giving ACE a home.
68 //  Microsoft       - For giving us such a wonderful crash free OS.
69 //  id              - Need I say more.
70 //
71 //  And to all the other testers, pathers, and players and people
72 //  who I can't remember who the heck they were, but helped out.
73 //
74 ///////////////////////////////////////////////////////////////////////
75 
76 ///////////////////////////////////////////////////////////////////////
77 //  acebot_movement.c - This file contains all of the
78 //                      movement routines for the ACE bot
79 //
80 ///////////////////////////////////////////////////////////////////////
81 
82 #ifdef HAVE_CONFIG_H
83 #include "config.h"
84 #endif
85 
86 #include "game/g_local.h"
87 #include "acebot.h"
88 
89 ///////////////////////////////////////////////////////////////////////
90 // Checks if bot can move (really just checking the ground)
91 // Also, this is not a real accurate check, but does a
92 // pretty good job and looks for lava/slime.
93 ///////////////////////////////////////////////////////////////////////
ACEMV_CanMove(edict_t * self,int direction)94 qboolean ACEMV_CanMove(edict_t *self, int direction)
95 {
96 	vec3_t forward, right;
97 	vec3_t offset,start,end;
98 	vec3_t angles;
99 	trace_t tr;
100 	gitem_t *vehicle;
101 
102 	vehicle = FindItemByClassname("item_bomber");
103 
104 	if (self->client->pers.inventory[ITEM_INDEX(vehicle)]) {
105 		return true; // yup, can move, we are in an air vehicle
106 	}
107 	vehicle = FindItemByClassname("item_strafer");
108 
109 	if (self->client->pers.inventory[ITEM_INDEX(vehicle)]) {
110 		return true; // yup, can move, we are in an air vehicle
111 	}
112 
113 	// Now check to see if move will move us off an edge
114 	VectorCopy(self->s.angles,angles);
115 
116 	if(direction == MOVE_LEFT)
117 		angles[1] += 90;
118 	else if(direction == MOVE_RIGHT)
119 		angles[1] -= 90;
120 	else if(direction == MOVE_BACK)
121 		angles[1] -=180;
122 
123 	// Set up the vectors
124 	AngleVectors (angles, forward, right, NULL);
125 
126 	VectorSet(offset, 36, 0, 24);
127 	G_ProjectSource (self->s.origin, offset, forward, right, start);
128 
129 	VectorSet(offset, 36, 0, -400);
130 	G_ProjectSource (self->s.origin, offset, forward, right, end);
131 
132 	tr = gi.trace(start, NULL, NULL, end, self,
133 			(CONTENTS_SOLID|CONTENTS_SLIME|CONTENTS_MIST|CONTENTS_LAVA|CONTENTS_WINDOW));
134 	if(tr.fraction > 0.3 || (tr.contents & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_MIST)) )
135 	{
136 		if(debug_mode)
137 			debug_printf("%s: move blocked\n",self->client->pers.netname);
138 		if(self->groundentity)
139 			self->s.angles[YAW] += random() * 180 - 90; //blocked, so try something else
140 		return false;
141 	}
142 
143 	return true; // yup, can move
144 }
145 
146 ///////////////////////////////////////////////////////////////////////
147 // Handle special cases of crouch/jump
148 //
149 // If the move is resolved here, this function returns
150 // true.
151 ///////////////////////////////////////////////////////////////////////
ACEMV_SpecialMove(edict_t * self,usercmd_t * ucmd)152 qboolean ACEMV_SpecialMove(edict_t *self, usercmd_t *ucmd)
153 {
154 	vec3_t dir,forward,right,start,end,offset;
155 	vec3_t top;
156 	trace_t tr;
157 	short mSpeed;
158 
159 	if(g_tactical->integer)
160 		mSpeed = 200;
161 	else
162 		mSpeed = 400;
163 
164 	// Get current direction
165 	VectorCopy(self->client->ps.viewangles,dir);
166 	dir[YAW] = self->s.angles[YAW];
167 	AngleVectors (dir, forward, right, NULL);
168 
169 	VectorSet(offset, 18, 0, 0);
170 	G_ProjectSource (self->s.origin, offset, forward, right, start);
171 	offset[0] += 18;
172 	G_ProjectSource (self->s.origin, offset, forward, right, end);
173 
174 	// trace it
175 	start[2] += 18; // so they are not jumping all the time
176 	end[2] += 18;
177 	tr = gi.trace (start, self->mins, self->maxs, end, self, MASK_MONSTERSOLID);
178 
179 	if(tr.allsolid)
180 	{
181 		// Check for crouching
182 		start[2] -= 14;
183 		end[2] -= 14;
184 
185 		// Set up for crouching check
186 		VectorCopy(self->maxs,top);
187 		top[2] = 0.0; // crouching height
188 		tr = gi.trace (start, self->mins, top, end, self, MASK_PLAYERSOLID);
189 
190 		// Crouch
191 		if(!tr.allsolid)
192 		{
193 			if(ACEMV_CanMove(self, MOVE_FORWARD))
194 				ucmd->forwardmove = mSpeed;
195 			else if(ACEMV_CanMove(self, MOVE_BACK))
196 				ucmd->forwardmove = -mSpeed;
197 			ucmd->upmove = -400;
198 			return true;
199 		}
200 
201 		// Check for jump
202 		start[2] += 32;
203 		end[2] += 32;
204 		tr = gi.trace (start, self->mins, self->maxs, end, self, MASK_MONSTERSOLID);
205 
206 		if(!tr.allsolid)
207 		{
208 			if(ACEMV_CanMove(self, MOVE_FORWARD))
209 				ucmd->forwardmove = mSpeed;
210 			else if(ACEMV_CanMove(self, MOVE_BACK))
211 				ucmd->forwardmove = -mSpeed;
212 			ucmd->upmove = 400;
213 			return true;
214 		}
215 	}
216 
217 	return false; // We did not resolve a move here
218 }
219 
220 
221 ///////////////////////////////////////////////////////////////////////
222 // Checks for obstructions in front of bot
223 //
224 // This is a function I created origianlly for ACE that
225 // tries to help steer the bot around obstructions.
226 //
227 // If the move is resolved here, this function returns true.
228 ///////////////////////////////////////////////////////////////////////
ACEMV_CheckEyes(edict_t * self,usercmd_t * ucmd)229 qboolean ACEMV_CheckEyes(edict_t *self, usercmd_t *ucmd)
230 {
231 	vec3_t  forward, right;
232 	vec3_t  leftstart, rightstart,focalpoint;
233 	vec3_t  upstart,upend;
234 	vec3_t  dir,offset;
235 	short mSpeed;
236 	trace_t traceRight,traceLeft,traceUp, traceFront; // for eyesight
237 
238 	if(g_tactical->integer)
239 		mSpeed = 200;
240 	else
241 		mSpeed = 400;
242 
243 	// Get current angle and set up "eyes"
244 
245 /* make sure bot's "eyes" are straight ahead
246  * something is going wrong after rocket jump, and "eyes" are always down
247  * and bot runs in circle.
248  */
249 	self->s.angles[PITCH] = 0.0f;
250 
251 	VectorCopy(self->s.angles,dir);
252 	AngleVectors (dir, forward, right, NULL);
253 
254 	// Let them move to targets by walls
255 	if(!self->movetarget)
256 		VectorSet(offset,200,0,4); // focalpoint
257 	else
258 		VectorSet(offset,36,0,4); // focalpoint
259 
260 	G_ProjectSource (self->s.origin, offset, forward, right, focalpoint);
261 
262 	// Check from self to focalpoint
263 	// Ladder code
264 	VectorSet(offset,36,0,0); // set as high as possible
265 	G_ProjectSource (self->s.origin, offset, forward, right, upend);
266 	traceFront = gi.trace(self->s.origin, self->mins, self->maxs, upend, self, BOTMASK_OPAQUE);
267 
268 	if ( traceFront.contents & CONTENTS_LADDER )
269 	{
270 		ucmd->upmove = 400;
271 		if(ACEMV_CanMove(self, MOVE_FORWARD))
272 			ucmd->forwardmove = mSpeed;
273 		return true;
274 	}
275 
276 	// If this check fails we need to continue on with more detailed checks
277 	if ( traceFront.fraction >= 1.0f )
278 	{
279 		if ( ACEMV_CanMove( self, MOVE_FORWARD ) )
280 			ucmd->forwardmove = mSpeed; //only try forward, bot should be looking to move in direction of eyes
281 		return true;
282 	}
283 
284 	VectorSet(offset, 0, 18, 4);
285 	G_ProjectSource (self->s.origin, offset, forward, right, leftstart);
286 
287 	offset[1] -= 36; // want to make sure this is correct
288 	//VectorSet(offset, 0, -18, 4);
289 	G_ProjectSource (self->s.origin, offset, forward, right, rightstart);
290 
291 	traceRight = gi.trace(rightstart, NULL, NULL, focalpoint, self, BOTMASK_OPAQUE);
292 	traceLeft = gi.trace(leftstart, NULL, NULL, focalpoint, self, BOTMASK_OPAQUE);
293 
294 	// Wall checking code, this will degenerate progressivly so the least cost
295 	// check will be done first.
296 
297 	// If open space move ok
298 	if(traceRight.fraction != 1 || traceLeft.fraction != 1 || strcmp(traceLeft.ent->classname,"func_door")!=0)
299 	{
300 		// Special uppoint logic to check for slopes/stairs/jumping etc.
301 		VectorSet(offset, 0, 18, 24);
302 		G_ProjectSource (self->s.origin, offset, forward, right, upstart);
303 
304 		VectorSet(offset,0,0,200); // scan for height above head
305 		G_ProjectSource (self->s.origin, offset, forward, right, upend);
306 		traceUp = gi.trace(upstart, NULL, NULL, upend, self, BOTMASK_OPAQUE);
307 
308 		VectorSet(offset,200,0,200*traceUp.fraction-5); // set as high as possible
309 		G_ProjectSource (self->s.origin, offset, forward, right, upend);
310 		traceUp = gi.trace(upstart, NULL, NULL, upend, self, BOTMASK_OPAQUE);
311 
312 		// If the upper trace is not open, we need to turn.
313 		if(traceUp.fraction < 1.0f )
314 		{
315 			if(traceRight.fraction > traceLeft.fraction)
316 				self->s.angles[YAW] += (1.0 - traceLeft.fraction) * 45.0;
317 			else
318 				self->s.angles[YAW] += -(1.0 - traceRight.fraction) * 45.0;
319 			if(ACEMV_CanMove(self, MOVE_FORWARD))
320 				ucmd->forwardmove = mSpeed;
321 			return true;
322 		}
323 	}
324 
325 	return false;
326 }
327 
328 ///////////////////////////////////////////////////////////////////////
329 // Make the change in angles a little more gradual, not so snappy
330 // Subtle, but noticeable.
331 //
332 // Modified from the original id ChangeYaw code...
333 ///////////////////////////////////////////////////////////////////////
ACEMV_ChangeBotAngle(edict_t * ent)334 void ACEMV_ChangeBotAngle (edict_t *ent)
335 {
336 	float	ideal_yaw;
337 	float   ideal_pitch;
338 	float	current_yaw;
339 	float   current_pitch;
340 	float	move;
341 	float	speed;
342 	vec3_t  ideal_angle;
343 
344 	// Normalize the move angle first
345 	VectorNormalize(ent->move_vector);
346 
347 	current_yaw = anglemod(ent->s.angles[YAW]);
348 	current_pitch = anglemod(ent->s.angles[PITCH]);
349 
350 	vectoangles (ent->move_vector, ideal_angle);
351 
352 	ideal_yaw = anglemod(ideal_angle[YAW]);
353 	ideal_pitch = anglemod(ideal_angle[PITCH]);
354 
355 	// Yaw
356 	if (current_yaw != ideal_yaw)
357 	{
358 		move = ideal_yaw - current_yaw;
359 		speed = ent->yaw_speed;
360 		if (ideal_yaw > current_yaw)
361 		{
362 			if (move >= 180.0f)
363 				move = move - 360.0f;
364 		}
365 		else
366 		{
367 			if (move <= -180.0f)
368 				move = move + 360.0f;
369 		}
370 		if (move > 0.0f)
371 		{
372 			if (move > speed)
373 				move = speed;
374 		}
375 		else
376 		{
377 			if (move < -speed)
378 				move = -speed;
379 		}
380 		ent->s.angles[YAW] = anglemod (current_yaw + move);
381 	}
382 
383 	// Pitch
384 	if (current_pitch != ideal_pitch)
385 	{
386 		move = ideal_pitch - current_pitch;
387 		speed = ent->yaw_speed;
388 		if (ideal_pitch > current_pitch)
389 		{
390 			if (move >= 180.0f)
391 				move = move - 360.0f;
392 		}
393 		else
394 		{
395 			if (move <= -180.0f)
396 				move = move + 360.0f;
397 		}
398 		if (move > 0.0f)
399 		{
400 			if (move > speed)
401 				move = speed;
402 		}
403 		else
404 		{
405 			if (move < -speed)
406 				move = -speed;
407 		}
408 		ent->s.angles[PITCH] = anglemod (current_pitch + move);
409 	}
410 }
411 
412 ///////////////////////////////////////////////////////////////////////
413 // Set bot to move to it's movetarget. (following node path)
414 ///////////////////////////////////////////////////////////////////////
ACEMV_MoveToGoal(edict_t * self,usercmd_t * ucmd)415 void ACEMV_MoveToGoal(edict_t *self, usercmd_t *ucmd)
416 {
417 	short mSpeed;
418 
419 	if(g_tactical->integer)
420 		mSpeed = 200;
421 	else
422 		mSpeed = 400;
423 
424 	// If a rocket or grenade is around deal with it
425 	// Simple, but effective (could be rewritten to be more accurate)
426 	if(strcmp(self->movetarget->classname,"rocket") == 0 ||
427 	   strcmp(self->movetarget->classname,"grenade") == 0 ||
428 	   strcmp(self->movetarget->classname,"seeker") == 0 )
429 	{
430 		if(debug_mode)
431 			debug_printf("%s: Oh crap a rocket!\n",self->client->pers.netname);
432 
433 		if(strcmp(self->movetarget->classname,"seeker") == 0)
434 		{
435 			VectorSubtract (self->s.origin, self->movetarget->s.origin, self->move_vector);
436 			ACEMV_ChangeBotAngle(self);
437 
438 			//turn and run like hell away from it
439 			//to do - needs improvement - bot should run when getting hit by seeker as well
440 			if(ACEMV_CanMove(self, MOVE_FORWARD))
441 				ucmd->forwardmove = mSpeed;
442 		}
443 		else
444 		{
445 			VectorSubtract (self->movetarget->s.origin, self->s.origin, self->move_vector);
446 			ACEMV_ChangeBotAngle(self);
447 
448 			// strafe left/right
449 			if(rand()%1 && ACEMV_CanMove(self, MOVE_LEFT))
450 					ucmd->sidemove = -mSpeed;
451 			else if(ACEMV_CanMove(self, MOVE_RIGHT))
452 					ucmd->sidemove = mSpeed;
453 		}
454 		return;
455 
456 	}
457 	else
458 	{
459 		// Set bot's movement direction
460 		VectorSubtract (self->movetarget->s.origin, self->s.origin, self->move_vector);
461 		ACEMV_ChangeBotAngle(self);
462 
463 		//try moving forward, if blocked, strafe around it if possible.
464 		if(ACEMV_CanMove(self, MOVE_FORWARD))
465 			ucmd->forwardmove = mSpeed;
466 		else if(ACEMV_CanMove(self, MOVE_BACK))
467 				ucmd->forwardmove = -mSpeed;
468 		else if(ACEMV_CanMove(self, MOVE_RIGHT))
469 				ucmd->sidemove = mSpeed;
470 		else if(ACEMV_CanMove(self, MOVE_LEFT))
471 				ucmd->sidemove = -mSpeed;
472 		return;
473 	}
474 }
475 
476 ///////////////////////////////////////////////////////////////////////
477 // Main movement code. (following node path)
478 ///////////////////////////////////////////////////////////////////////
ACEMV_Move(edict_t * self,usercmd_t * ucmd)479 void ACEMV_Move(edict_t *self, usercmd_t *ucmd)
480 {
481 	vec3_t dist;
482 	int current_node_type=-1;
483 	int next_node_type=-1;
484 	int i;
485 	float c;
486 	short mSpeed;
487 
488 	if(g_tactical->integer)
489 		mSpeed = 200;
490 	else
491 		mSpeed = 400;
492 
493 	// Get current and next node back from nav code.
494 	if(!ACEND_FollowPath(self))
495 	{
496 		self->state = STATE_WANDER;
497 		self->wander_timeout = level.time + 1.0;
498 		return;
499 	}
500 
501 	if(!self->groundentity)
502 		return;
503 
504 	current_node_type = nodes[self->current_node].type;
505 	next_node_type = nodes[self->next_node].type;
506 
507 	///////////////////////////
508 	// Move To Goal
509 	///////////////////////////
510 	if (self->movetarget)
511 		ACEMV_MoveToGoal(self,ucmd);
512 
513 	////////////////////////////////////////////////////////
514 	// Platforms
515 	///////////////////////////////////////////////////////
516 	if(current_node_type != NODE_PLATFORM && next_node_type == NODE_PLATFORM)
517 	{
518 		// check to see if lift is down?
519 		for(i=0;i<num_items;i++)
520 			if(item_table[i].node == self->next_node)
521 				if(item_table[i].ent->moveinfo.state != STATE_BOTTOM)
522 				    return; // Wait for elevator
523 	}
524 	if(current_node_type == NODE_PLATFORM && next_node_type == NODE_PLATFORM)
525 	{
526 		// Move to the center
527 		self->move_vector[2] = 0; // kill z movement
528 		if(VectorLength(self->move_vector) > 10)
529 			ucmd->forwardmove = 200; // walk to center
530 
531 		ACEMV_ChangeBotAngle(self);
532 
533 		return; // No move, riding elevator
534 	}
535 
536 	////////////////////////////////////////////////////////
537 	// Jumpto Nodes
538 	///////////////////////////////////////////////////////
539 	if(next_node_type == NODE_JUMP ||
540 	  (current_node_type == NODE_JUMP && next_node_type != NODE_ITEM && nodes[self->next_node].origin[2] > self->s.origin[2]))
541 	{
542 		// Set up a jump move
543 		ucmd->forwardmove = mSpeed;
544 		ucmd->upmove = 400;
545 
546 		ACEMV_ChangeBotAngle(self);
547 
548 		VectorCopy(self->move_vector, dist);
549 		VectorScale(dist, 440, self->velocity);
550 
551 		return;
552 	}
553 
554 	////////////////////////////////////////////////////////
555 	// Ladder Nodes
556 	///////////////////////////////////////////////////////
557 	if(next_node_type == NODE_LADDER && nodes[self->next_node].origin[2] > self->s.origin[2])
558 	{
559 		// Otherwise move as fast as we can
560 		ucmd->forwardmove = mSpeed;
561 		self->velocity[2] = 320;
562 
563 		ACEMV_ChangeBotAngle(self);
564 
565 		return;
566 
567 	}
568 	// If getting off the ladder
569 	if(current_node_type == NODE_LADDER && next_node_type != NODE_LADDER &&
570 	   nodes[self->next_node].origin[2] > self->s.origin[2])
571 	{
572 		ucmd->forwardmove = mSpeed;
573 		ucmd->upmove = 200;
574 		self->velocity[2] = 200;
575 		ACEMV_ChangeBotAngle(self);
576 		return;
577 	}
578 
579 	////////////////////////////////////////////////////////
580 	// Water Nodes
581 	///////////////////////////////////////////////////////
582 	if(current_node_type == NODE_WATER)
583 	{
584 		// We need to be pointed up/down
585 		ACEMV_ChangeBotAngle(self);
586 
587 		// If the next node is not in the water, then move up to get out.
588 		if(next_node_type != NODE_WATER && !(gi.pointcontents(nodes[self->next_node].origin) & MASK_WATER)) // Exit water
589 			ucmd->upmove = 400;
590 
591 		ucmd->forwardmove = 300;
592 
593 		return;
594 	}
595 
596 	// Falling off ledge?
597 	if(!self->groundentity)
598 	{
599 		ACEMV_ChangeBotAngle(self);
600 
601 		self->velocity[0] = self->move_vector[0] * 360;
602 		self->velocity[1] = self->move_vector[1] * 360;
603 
604 		return;
605 	}
606 
607 	// Check to see if stuck, and if so try to free us
608 	// Also handles crouching
609  	if(VectorLength(self->velocity) < 37)
610 	{
611 		// Keep a random factor just in case....
612 		if(random() > 0.1 && ACEMV_SpecialMove(self, ucmd))
613 			return;
614 
615 		self->s.angles[YAW] += random() * 180 - 90;
616 		// Try moving foward, if not, try to strafe around obstacle
617 		if(ACEMV_CanMove(self, MOVE_FORWARD))
618 			ucmd->forwardmove = mSpeed;
619 		else if(ACEMV_CanMove(self, MOVE_BACK))
620 			ucmd->forwardmove = -mSpeed;
621 		else if(ACEMV_CanMove(self, MOVE_RIGHT))
622 				ucmd->sidemove = mSpeed;
623 		else if(ACEMV_CanMove(self, MOVE_LEFT))
624 				ucmd->sidemove = -mSpeed;
625 		return;
626 	}
627 
628 	// Otherwise move as fast as we can
629 	if(ACEMV_CanMove(self, MOVE_FORWARD))
630 		ucmd->forwardmove = mSpeed;
631 
632 	if(self->skill == 3)
633 	{	//ultra skill level(will be 3)
634 		c = random();
635 
636 		if(!self->in_deathball && grapple->value && c <= .7)
637 		{	//use the grapple once in awhile to pull itself around
638 
639 			if(self->client->ctf_grapplestate == CTF_GRAPPLE_STATE_HANG)
640 			{
641 				CTFPlayerResetGrapple(self);
642 				ACEMV_ChangeBotAngle(self);
643 				return;
644 			}
645 
646 			ACEMV_ChangeBotAngle(self);
647 			ACEIT_ChangeWeapon(self,FindItem("grapple"));
648 			ucmd->buttons = BUTTON_ATTACK;
649 			ACEMV_ChangeBotAngle(self);
650 			return;
651 		}
652 		else if(self->client->ctf_grapplestate != CTF_GRAPPLE_STATE_PULL &&
653 			self->client->ctf_grapplestate != CTF_GRAPPLE_STATE_HANG)
654 		{	//don't interrupt a pull
655 			float weight;
656 			int strafeJump = false;
657 
658 			//Strafejumping should only occur now if a bot is far enough from a node
659 			//and not wandering.  We really don't want them to jump around when wandering
660 			//as it seems to hinder locating a goal.
661 
662 			VectorSubtract(self->s.origin, nodes[self->current_node].origin, dist);
663 			weight = VectorLength( dist );
664 			if(weight > 300)
665 				strafeJump = true;
666 
667 			if(strafeJump)
668 			{
669 				if(c > .7)
670 					ucmd->upmove = 400; //jump around the level
671 
672 				if(c > 0.9 && ACEMV_CanMove(self, MOVE_LEFT))
673 					ucmd->sidemove = -200; //strafejump left(was -400)
674 
675 				else if(c > 0.8 && ACEMV_CanMove(self, MOVE_RIGHT))
676 					ucmd->sidemove = 200; //strafejump right(was 400)
677 			}
678 		}
679 
680 		//Now if we have the Alien Smartgun, drop some prox mines :)
681 		if (self->client->pers.weapon == FindItem("alien smartgun") && c < 0.2)
682 			ucmd->buttons = BUTTON_ATTACK2;
683 	}
684 
685 	ACEMV_ChangeBotAngle(self);
686 }
687 
688 
689 ///////////////////////////////////////////////////////////////////////
690 // Wandering code (based on old ACE movement code)
691 ///////////////////////////////////////////////////////////////////////
ACEMV_Wander(edict_t * self,usercmd_t * ucmd)692 void ACEMV_Wander(edict_t *self, usercmd_t *ucmd)
693 {
694 	vec3_t  temp;
695 	float c;
696 	short mSpeed;
697 
698 	if(g_tactical->integer)
699 		mSpeed = 200;
700 	else
701 		mSpeed = 400;
702 
703 	// Do not move
704 	if(self->next_move_time > level.time)
705 		return;
706 
707 	// Special check for elevators, stand still until the ride comes to a complete stop.
708 	if(self->groundentity != NULL && self->groundentity->use == Use_Plat)
709 		if(self->groundentity->moveinfo.state == STATE_UP ||
710 		   self->groundentity->moveinfo.state == STATE_DOWN) // only move when platform not
711 		{
712 			self->velocity[0] = 0;
713 			self->velocity[1] = 0;
714 			self->velocity[2] = 0;
715 			self->next_move_time = level.time + 0.5;
716 			return;
717 		}
718 
719 	// Is there a target to move to
720 	if (self->movetarget)
721 		ACEMV_MoveToGoal(self,ucmd);
722 
723 	////////////////////////////////
724 	// Swimming?
725 	////////////////////////////////
726 	VectorCopy(self->s.origin,temp);
727 	temp[2]+=24;
728 
729 	if(gi.pointcontents (temp) & MASK_WATER)
730 	{
731 		// If drowning and no node, move up
732 		if(self->client->next_drown_time > 0)
733 		{
734 			ucmd->upmove = 1;
735 			self->s.angles[PITCH] = -45;
736 		}
737 		else
738 			ucmd->upmove = 15;
739 
740 		ucmd->forwardmove = 300;
741 	}
742 	else
743 		self->client->next_drown_time = 0; // probably shound not be messing with this, but
744 
745 	////////////////////////////////
746 	// Lava?
747 	////////////////////////////////
748 	temp[2]-=48;
749 	if(gi.pointcontents(temp) & (CONTENTS_LAVA|CONTENTS_SLIME))
750 	{
751 		//	safe_bprintf(PRINT_MEDIUM,"lava jump\n");
752 		self->s.angles[YAW] += random() * 360 - 180;
753 		ucmd->forwardmove = mSpeed;
754 		ucmd->upmove = 400;
755 		return;
756 	}
757 
758 	if(ACEMV_CheckEyes(self,ucmd))
759 		return;
760 
761 	// Check for special movement if we have a normal move (have to test)
762  	if(VectorLength(self->velocity) < 37)
763 	{
764 		/*if(random() > 0.1 && ACEMV_SpecialMove(self,ucmd))
765 			return;*/ //removed this because when wandering, the last thing you want is bots jumping
766 		//over things and going off ledges.  It's better for them to just bounce around the map.
767 
768 		self->s.angles[YAW] += random() * 180 - 90;
769 
770 		// Try to move forward, if blocked, try to strafe around obstacle
771 		if(ACEMV_CanMove(self, MOVE_FORWARD))
772 			ucmd->forwardmove = mSpeed;
773 		else if(ACEMV_CanMove(self, MOVE_BACK))
774 			ucmd->forwardmove = -mSpeed;
775 		else if(ACEMV_CanMove(self, MOVE_RIGHT))
776 				ucmd->sidemove = mSpeed;
777 		else if(ACEMV_CanMove(self, MOVE_LEFT))
778 				ucmd->sidemove = -mSpeed;
779 
780 		if(!M_CheckBottom(self) || !self->groundentity) // if there is ground continue, otherwise wait for next move.
781 		{
782 			if(ACEMV_CanMove(self, MOVE_FORWARD))
783 				ucmd->forwardmove = mSpeed;
784 		}
785 
786 		return;
787 	}
788 
789 	if(ACEMV_CanMove(self, MOVE_FORWARD))
790 		ucmd->forwardmove = mSpeed;
791 
792 	if(self->skill == 3)
793 	{ //ultra skill level(will be 3)
794 		c = random();
795 
796 		//If we have the Alien Smartgun, drop some prox mines :)
797 		if (self->client->pers.weapon == FindItem("alien smartgun") && c < 0.2)
798 			ucmd->buttons = BUTTON_ATTACK2;
799 	}
800 }
801 
802 /**
803  * @brief  Apply some imprecision to an attacking bot's aim
804  *
805  * @detail Uses a randomly oriented X,Y vector with a random length
806  *         plus an offset, which leaves a hole in the center of the
807  *         target area.
808  *
809  * @param self entity for the bot
810  * @param pdx  x for targeted enemy's origin
811  * @param pdy  y for targeted enemy's origin
812  *
813  * @return  *pdx and *pdy altered
814  */
815 /*
816  * constants for target fuzzification
817  */
818 static const float ktgt_scale = 250.0f; //scaling factor
819 static const float ktgt_ofs   = 20.0f;  //radius of hole
820 /* scaling for weap skill for bot cfg skill 0, 1, 2 & 3 */
821 static const float ktgt_skill[4] =
822 	{ 0.7f, 0.8f, 0.9f, 1.0f }; //accuracy scaling by skill
823 //accuracy factor = ktgt_skill[0] * self->accuracy minimum clamp
824 static const float ktgt_acc = 0.35;
825 
fuzzy_target(edict_t * self,float * pdx,float * pdy)826 static void fuzzy_target( edict_t *self, float *pdx, float *pdy )
827 {
828 	float accuracy;
829 	float random_r;
830 	float radius;
831 	float angle;
832 	float ca,sa;
833 	float dx,dy;
834 
835 	/*
836 	 * self->accuracy is weapon specific accuracy from bot .cfg file
837 	 */
838 	accuracy = self->accuracy;
839 	if ( accuracy < 0.5f )
840 		accuracy = 0.5f;
841 	else if ( accuracy > 1.0f )
842 		accuracy = 1.0f;
843 
844 	//calc weap accuracy factor with scaling on bot skill
845 	switch ( self->skill )
846 	{
847 	case 0:
848 		accuracy *= ktgt_skill[0];
849 		break;
850 	case 1:
851 	default:
852 		accuracy *= ktgt_skill[1];
853 		break;
854 	case 2:
855 		accuracy *= ktgt_skill[2];
856 		break;
857 	case 3:
858 		//override 3->2 for CTF
859 		accuracy *= (ctf->integer) ? ktgt_skill[2] : ktgt_skill[3];
860 		break;
861 	}
862 
863 	//radius calc
864 	random_r = ktgt_scale * crandom();
865 	radius =  (random_r * ( ktgt_acc / accuracy)) + ktgt_ofs;
866 	//angle calc
867 	angle = random() * 2.0f * M_PI;
868 	fast_sincosf( angle, &sa, &ca );
869 	//apply delta to target
870 	*pdx += dx = ca * radius;
871 	*pdy += dy = sa * radius;
872 
873 //	if ( debug_mode )
874 //	{
875 //		gi.dprintf("{\t%0.2f\t%0.2f\tacc%0.1f\t%i}\n",
876 //				dx, dy, accuracy, self->skill );
877 //	}
878 
879 }
880 
881 ///////////////////////////////////////////////////////////////////////
882 // Attack movement routine
883 ///////////////////////////////////////////////////////////////////////
ACEMV_Attack(edict_t * self,usercmd_t * ucmd)884 void ACEMV_Attack (edict_t *self, usercmd_t *ucmd)
885 {
886 	float c, d;
887 	vec3_t  target;
888 	vec3_t  angles;
889 	vec3_t down;
890 	int strafespeed;
891 	float jump_thresh;
892 	float crouch_thresh;
893 	gitem_t *vehicle;
894 	float range = 0.0f;
895 	vec3_t v;
896 	qboolean use_fuzzy_aim;
897 	short mSpeed;
898 
899 	if(g_tactical->integer)
900 		mSpeed = 200;
901 	else
902 		mSpeed = 400;
903 
904 	ucmd->buttons = 0;
905 	use_fuzzy_aim = true; // unless overridden by special cases
906 	if ( dmflags->integer & DF_BOT_FUZZYAIM )
907 	{ // when bit is set it means fuzzy aim is disabled. for testing mostly.
908 		use_fuzzy_aim = false;
909 	}
910 
911 	vehicle = FindItemByClassname("item_bomber");
912 
913 	if (self->client->pers.inventory[ITEM_INDEX(vehicle)])
914 	{
915 		//if we are too low, don't shoot, and move up.  Should be fairly simple, right?
916 		if(self->enemy->s.origin[2] >= self->s.origin[2] - 128) { //we want to be well above our target
917 			ucmd->upmove += 400;
918 			// face the enemy while moving up
919 			VectorCopy(self->enemy->s.origin,target);
920 			VectorSubtract (target, self->s.origin, self->move_vector);
921 			vectoangles (self->move_vector, angles);
922 			VectorCopy(angles,self->s.angles);
923 			return;
924 		}
925 	}
926 
927 	switch ( self->skill )
928 	{ // set up the skill levels
929 	case 0:
930 		strafespeed   = 300;
931 		jump_thresh   = 1.0f;  // never jump
932 		crouch_thresh = 0.0f;  // never crouch
933 		break;
934 
935 	case 1:
936 	default:
937 		strafespeed   = 400;
938 		jump_thresh   = 0.95f;
939 		crouch_thresh = 0.85f; // crouch much
940 		break;
941 
942 	case 2:
943 	case 3:
944 		strafespeed = 800;
945 		if ( !joustmode->integer )
946 		{
947 			jump_thresh   = 0.8f;
948 			crouch_thresh = 0.7f;
949 		}
950 		else
951 		{
952 			jump_thresh   = 0.5f; // want to jump a whole lot more in joust mode
953 			crouch_thresh = 0.4f; // and crouch less
954 		}
955 		break;
956 	}
957 
958 	d = random(); // for skill 0 movement
959 	c = random(); // for strafing, jumping, crouching
960 
961 	// violator attack
962 	if ( self->client->pers.weapon == FindItem( "Violator" ) )
963 	{
964 		use_fuzzy_aim = false; // avoid potential odd melee attack behaviour
965 		if ( ACEMV_CanMove( self, MOVE_FORWARD ) )
966 			ucmd->forwardmove += 400; //lunge at enemy
967 		goto attack;
968 	}
969 
970 	//machinegun/blaster/beamgun strafing for level 2/3 bots
971 	if ( !joustmode->value
972 			&& self->skill >= 2
973 			&& (self->client->pers.weapon == FindItem( "Blaster" )
974 					|| self->client->pers.weapon == FindItem( "Alien Blaster" )
975 					|| self->client->pers.weapon == FindItem( "Pulse Rifle" )
976 					|| self->client->pers.weapon == FindItem( "Disruptor" )))
977 	{
978 		//strafe no matter what
979 		if ( c < 0.5f && ACEMV_CanMove( self, MOVE_LEFT ) )
980 		{
981 			ucmd->sidemove -= mSpeed;
982 		}
983 		else if ( c < 1.0f && ACEMV_CanMove( self, MOVE_RIGHT ) )
984 		{
985 			ucmd->sidemove += mSpeed;
986 		}
987 		else
988 		{ //don't want high skill level bots just standing around
989 			goto standardmove;
990 		}
991 
992 		//allow for some circle strafing
993 		if ( self->health < 50 && ACEMV_CanMove( self, MOVE_BACK ) )
994 		{
995 			ucmd->forwardmove -= mSpeed;
996 		}
997 		else if ( c < 0.6f && ACEMV_CanMove( self, MOVE_FORWARD ) )
998 		{ //keep this at default, not make them TOO hard
999 			ucmd->forwardmove += mSpeed;
1000 		}
1001 		else if ( c < 0.8f && ACEMV_CanMove( self, MOVE_BACK ) )
1002 		{
1003 			ucmd->forwardmove -= mSpeed;
1004 		}
1005 		goto attack;
1006 		//skip any jumping or crouching
1007 	}
1008 
1009 	if ( self->skill == 0 && d < 0.9f )
1010 		goto attack; //skill 0 bots will barely move while firing
1011 
1012 standardmove:
1013 
1014 	if ( c < 0.2f && ACEMV_CanMove( self, MOVE_LEFT ) )
1015 	{
1016 		ucmd->sidemove -= strafespeed;
1017 		//300 for low skill 800 for hardest(3 levels?)
1018 	}
1019 	else if ( c < 0.4f && ACEMV_CanMove( self, MOVE_RIGHT ) )
1020 	{
1021 		ucmd->sidemove += strafespeed;
1022 	}
1023 
1024 	if ( self->health < 50 && ACEMV_CanMove( self, MOVE_BACK ) )
1025 	{ //run away if wounded
1026 		ucmd->forwardmove -= mSpeed;
1027 	}
1028 	else if ( c < 0.6f && ACEMV_CanMove( self, MOVE_FORWARD ) )
1029 	{ //keep this at default, not make them TOO hard
1030 		ucmd->forwardmove += mSpeed;
1031 	}
1032 	else if ( c < 0.8f && ACEMV_CanMove( self, MOVE_BACK ) )
1033 	{
1034 		ucmd->forwardmove -= mSpeed;
1035 	}
1036 
1037 	c = random(); //really mix this up some
1038 	if ( self->health >= 50 && c < crouch_thresh )
1039 	{
1040 		ucmd->upmove -= 200;
1041 	}
1042 	else if ( c > jump_thresh )
1043 	{
1044 #ifndef ALTERIA
1045 		c = random();
1046 		if ( self->health >= 70
1047 				&& self->skill >= 2
1048 		        && !self->in_vehicle && !self->in_deathball
1049 		        && ACEIT_ChangeWeapon( self, FindItem( "Rocket Launcher" ) )
1050 		        && c < 0.6f )
1051 		{ // Rocket Jump
1052 			self->s.angles[PITCH] = 90.0f;
1053 			AngleVectors( self->s.angles, down, NULL, NULL );
1054 			fire_rocket( self, self->s.origin, down, 200, 650, 120, 120 );
1055 			ucmd->upmove += 200;
1056 			self->s.angles[PITCH] = 0.0f;
1057 			if ( (!(dmflags->integer & DF_INFINITE_AMMO))
1058 			        && !rocket_arena->integer && !insta_rockets->integer )
1059 			{
1060 				self->client->pers.inventory[self->client->ammo_index]--;
1061 			}
1062 			return;
1063 		}
1064 		else
1065 #endif
1066 		{ // Normal Jump
1067 			ucmd->upmove += 200;
1068 		}
1069 	}
1070 
1071 attack:
1072 	// Set the attack
1073 	if(ACEAI_CheckShot(self))
1074 	{
1075 		//bot is taking a shot, lose spawn protection
1076 		// and calculate distance to enemy
1077 		range = 0.0f;
1078 		if(self->enemy)
1079 		{
1080 			self->client->spawnprotected = false;
1081 			VectorSubtract (self->s.origin, self->enemy->s.origin, v);
1082 			range = VectorLength(v);
1083 			if ( range < 32.0f )
1084 			{ // point blank range, avoid potentially odd behaviour
1085 				use_fuzzy_aim = false;
1086 			}
1087 		}
1088 
1089 		if(self->skill >= 2)
1090 		{	//skill 2/3 bots can use alt-fires!
1091 
1092 			// Base selection on distance.
1093 			ucmd->buttons = BUTTON_ATTACK;
1094 
1095 			if (self->client->pers.weapon == FindItem("Blaster") || self->client->pers.weapon == FindItem("Alien Blaster"))
1096 			{
1097 				if( range > 500)
1098 					ucmd->buttons = BUTTON_ATTACK2;
1099 				else
1100 					ucmd->buttons = BUTTON_ATTACK;
1101 			}
1102 
1103 			if (self->client->pers.weapon == FindItem("Alien Disruptor"))
1104 			{
1105 				if(range > 1000) {
1106 					ucmd->buttons = BUTTON_ATTACK2;
1107 					use_fuzzy_aim = false;
1108 					//make it more accurate, since he's sniping
1109 				}
1110 				else
1111 					ucmd->buttons = BUTTON_ATTACK;
1112 			}
1113 
1114 			if (self->client->pers.weapon == FindItem("Flame Thrower"))
1115 			{
1116 				if(range < 500)
1117 					ucmd->buttons = BUTTON_ATTACK;
1118 				else
1119 					ucmd->buttons = BUTTON_ATTACK2;
1120 			}
1121 
1122 			if (self->client->pers.weapon == FindItem("Pulse Rifle"))
1123 			{
1124 				if(range < 200)
1125 					ucmd->buttons = BUTTON_ATTACK2;
1126 				else
1127 					ucmd->buttons = BUTTON_ATTACK;
1128 			}
1129 
1130 			if (self->client->pers.weapon == FindItem("Disruptor"))
1131 			{
1132 				if(range < 300)
1133 					ucmd->buttons = BUTTON_ATTACK2;
1134 				else
1135 					ucmd->buttons = BUTTON_ATTACK;
1136 			}
1137 
1138 			if (self->client->pers.weapon == FindItem("Alien Vaporizer"))
1139 			{
1140 				if(range < 300)
1141 					ucmd->buttons = BUTTON_ATTACK2;
1142 				else
1143 					ucmd->buttons = BUTTON_ATTACK;
1144 			}
1145 
1146 			if (self->client->pers.weapon == FindItem("Minderaser"))
1147 			{
1148 				if(range < 400)
1149 					ucmd->buttons = BUTTON_ATTACK;
1150 				else
1151 					ucmd->buttons = BUTTON_ATTACK2;
1152 			}
1153 
1154 			//vehicle alt-fires
1155 			if (self->client->pers.weapon == FindItem("bomber")
1156 				|| self->client->pers.weapon == FindItem("strafer"))
1157 			{
1158 				if(range > 500)
1159 					ucmd->buttons = BUTTON_ATTACK2;
1160 				else
1161 					ucmd->buttons = BUTTON_ATTACK;
1162 			}
1163 			if (self->client->pers.weapon == FindItem("hover"))
1164 			{
1165 				if(range < 300)
1166 					ucmd->buttons = BUTTON_ATTACK2;
1167 				else
1168 					ucmd->buttons = BUTTON_ATTACK;
1169 			}
1170 		}
1171 		else
1172 		{
1173 			ucmd->buttons = BUTTON_ATTACK;
1174 		}
1175 	}
1176 	else
1177 	{ // not taking a shot
1178 		ucmd->buttons = 0;
1179 		use_fuzzy_aim = false;
1180 	}
1181 
1182 	// Aim
1183 	VectorCopy(self->enemy->s.origin,target);
1184 	if ( use_fuzzy_aim  )
1185 	{
1186 		fuzzy_target( self, &target[0], &target[1] );
1187 	}
1188 	// Set movement direction toward targeted enemy
1189 	VectorSubtract (target, self->s.origin, self->move_vector);
1190 	vectoangles (self->move_vector, angles);
1191 	VectorCopy(angles,self->s.angles);
1192 }
1193