1 ///////////////////////////////////////////////////////////////////////
2 //
3 //  ACE - Quake II Bot Base Code
4 //
5 //  Version 1.0
6 //
7 //  This file is Copyright(c), Steve Yeager 1998, All Rights Reserved
8 //
9 //
10 //	All other files are Copyright(c) Id Software, Inc.
11 //
12 //	Please see liscense.txt in the source directory for the copyright
13 //	information regarding those files belonging to Id Software, Inc.
14 //
15 //	Should you decide to release a modified version of ACE, you MUST
16 //	include the following text (minus the BEGIN and END lines) in the
17 //	documentation for your modification.
18 //
19 //	--- BEGIN ---
20 //
21 //	The ACE Bot is a product of Steve Yeager, and is available from
22 //	the ACE Bot homepage, at http://www.axionfx.com/ace.
23 //
24 //	This program is a modification of the ACE Bot, and is therefore
25 //	in NO WAY supported by Steve Yeager.
26 
27 //	This program MUST NOT be sold in ANY form. If you have paid for
28 //	this product, you should contact Steve Yeager immediately, via
29 //	the ACE Bot homepage.
30 //
31 //	--- END ---
32 //
33 //	I, Steve Yeager, hold no responsibility for any harm caused by the
34 //	use of this source code, especially to small children and animals.
35 //  It is provided as-is with no implied warranty or support.
36 //
37 //  I also wish to thank and acknowledge the great work of others
38 //  that has helped me to develop this code.
39 //
40 //  John Cricket    - For ideas and swapping code.
41 //  Ryan Feltrin    - For ideas and swapping code.
42 //  SABIN           - For showing how to do true client based movement.
43 //  BotEpidemic     - For keeping us up to date.
44 //  Telefragged.com - For giving ACE a home.
45 //  Microsoft       - For giving us such a wonderful crash free OS.
46 //  id              - Need I say more.
47 //
48 //  And to all the other testers, pathers, and players and people
49 //  who I can't remember who the heck they were, but helped out.
50 //
51 ///////////////////////////////////////////////////////////////////////
52 
53 ///////////////////////////////////////////////////////////////////////
54 //  acebot_movement.c - This file contains all of the
55 //                      movement routines for the ACE bot
56 //
57 ///////////////////////////////////////////////////////////////////////
58 
59 #include "../g_local.h"
60 #include "acebot.h"
61 
62 ///////////////////////////////////////////////////////////////////////
63 // Checks if bot can move (really just checking the ground)
64 // Also, this is not a real accurate check, but does a
65 // pretty good job and looks for lava/slime.
66 ///////////////////////////////////////////////////////////////////////
ACEMV_CanMove(edict_t * self,int direction)67 qboolean ACEMV_CanMove(edict_t *self, int direction)
68 {
69 	vec3_t forward, right;
70 	vec3_t offset,start,end;
71 	vec3_t angles;
72 	trace_t tr;
73 
74 	// Now check to see if move will move us off an edge
75 	VectorCopy(self->s.angles,angles);
76 
77 	if(direction == MOVE_LEFT)
78 		angles[1] += 90;
79 	else if(direction == MOVE_RIGHT)
80 		angles[1] -= 90;
81 	else if(direction == MOVE_BACK)
82 		angles[1] -=180;
83 
84 	// Set up the vectors
85 	AngleVectors (angles, forward, right, NULL);
86 
87 	VectorSet(offset, 36, 0, 24);
88 	G_ProjectSource (self->s.origin, offset, forward, right, start);
89 
90 	VectorSet(offset, 36, 0, -400);
91 	G_ProjectSource (self->s.origin, offset, forward, right, end);
92 
93 	tr = gi.trace(start, NULL, NULL, end, self, MASK_OPAQUE);
94 
95 	if((tr.fraction > 0.3 && tr.fraction != 1) || tr.contents & (CONTENTS_LAVA|CONTENTS_SLIME))
96 	{
97 		if(debug_mode)
98 			debug_printf("%s: move blocked\n",self->client->pers.netname);
99 		return false;
100 	}
101 
102 	return true; // yup, can move
103 }
104 
105 ///////////////////////////////////////////////////////////////////////
106 // Handle special cases of crouch/jump
107 //
108 // If the move is resolved here, this function returns
109 // true.
110 ///////////////////////////////////////////////////////////////////////
ACEMV_SpecialMove(edict_t * self,usercmd_t * ucmd)111 qboolean ACEMV_SpecialMove(edict_t *self, usercmd_t *ucmd)
112 {
113 	vec3_t dir,forward,right,start,end,offset;
114 	vec3_t top;
115 	trace_t tr;
116 
117 	// Get current direction
118 	VectorCopy(self->client->ps.viewangles,dir);
119 	dir[YAW] = self->s.angles[YAW];
120 	AngleVectors (dir, forward, right, NULL);
121 
122 	VectorSet(offset, 18, 0, 0);
123 	G_ProjectSource (self->s.origin, offset, forward, right, start);
124 	offset[0] += 18;
125 	G_ProjectSource (self->s.origin, offset, forward, right, end);
126 
127 	// trace it
128 	start[2] += 18; // so they are not jumping all the time
129 	end[2] += 18;
130 	tr = gi.trace (start, self->mins, self->maxs, end, self, MASK_MONSTERSOLID);
131 
132 	if(tr.allsolid)
133 	{
134 		// Check for crouching
135 		start[2] -= 14;
136 		end[2] -= 14;
137 
138 		// Set up for crouching check
139 		VectorCopy(self->maxs,top);
140 		top[2] = 0.0; // crouching height
141 		tr = gi.trace (start, self->mins, top, end, self, MASK_PLAYERSOLID);
142 
143 		// Crouch
144 		if(!tr.allsolid)
145 		{
146 			ucmd->forwardmove = 400;
147 			ucmd->upmove = -400;
148 			return true;
149 		}
150 
151 		// Check for jump
152 		start[2] += 32;
153 		end[2] += 32;
154 		tr = gi.trace (start, self->mins, self->maxs, end, self, MASK_MONSTERSOLID);
155 
156 		if(!tr.allsolid)
157 		{
158 			ucmd->forwardmove = 400;
159 			ucmd->upmove = 400;
160 			return true;
161 		}
162 	}
163 
164 	return false; // We did not resolve a move here
165 }
166 
167 
168 ///////////////////////////////////////////////////////////////////////
169 // Checks for obstructions in front of bot
170 //
171 // This is a function I created origianlly for ACE that
172 // tries to help steer the bot around obstructions.
173 //
174 // If the move is resolved here, this function returns true.
175 ///////////////////////////////////////////////////////////////////////
ACEMV_CheckEyes(edict_t * self,usercmd_t * ucmd)176 qboolean ACEMV_CheckEyes(edict_t *self, usercmd_t *ucmd)
177 {
178 	vec3_t  forward, right;
179 	vec3_t  leftstart, rightstart,focalpoint;
180 	vec3_t  upstart,upend;
181 	vec3_t  dir,offset;
182 
183 	trace_t traceRight,traceLeft,traceUp, traceFront; // for eyesight
184 
185 	// Get current angle and set up "eyes"
186 	VectorCopy(self->s.angles,dir);
187 	AngleVectors (dir, forward, right, NULL);
188 
189 	// Let them move to targets by walls
190 	if(!self->movetarget)
191 		VectorSet(offset,200,0,4); // focalpoint
192 	else
193 		VectorSet(offset,36,0,4); // focalpoint
194 
195 	G_ProjectSource (self->s.origin, offset, forward, right, focalpoint);
196 
197 	// Check from self to focalpoint
198 	// Ladder code
199 	VectorSet(offset,36,0,0); // set as high as possible
200 	G_ProjectSource (self->s.origin, offset, forward, right, upend);
201 	traceFront = gi.trace(self->s.origin, self->mins, self->maxs, upend, self, MASK_OPAQUE);
202 
203 	if(traceFront.contents & 0x8000000) // using detail brush here cuz sometimes it does not pick up ladders...??
204 	{
205 		ucmd->upmove = 400;
206 		ucmd->forwardmove = 400;
207 		return true;
208 	}
209 
210 	// If this check fails we need to continue on with more detailed checks
211 	if(traceFront.fraction == 1)
212 	{
213 		ucmd->forwardmove = 400;
214 		return true;
215 	}
216 
217 	VectorSet(offset, 0, 18, 4);
218 	G_ProjectSource (self->s.origin, offset, forward, right, leftstart);
219 
220 	offset[1] -= 36; // want to make sure this is correct
221 	//VectorSet(offset, 0, -18, 4);
222 	G_ProjectSource (self->s.origin, offset, forward, right, rightstart);
223 
224 	traceRight = gi.trace(rightstart, NULL, NULL, focalpoint, self, MASK_OPAQUE);
225 	traceLeft = gi.trace(leftstart, NULL, NULL, focalpoint, self, MASK_OPAQUE);
226 
227 	// Wall checking code, this will degenerate progressivly so the least cost
228 	// check will be done first.
229 
230 	// If open space move ok
231 	if(traceRight.fraction != 1 || traceLeft.fraction != 1 || strcmp(traceLeft.ent->classname,"func_door")!=0)
232 	{
233 		// Special uppoint logic to check for slopes/stairs/jumping etc.
234 		VectorSet(offset, 0, 18, 24);
235 		G_ProjectSource (self->s.origin, offset, forward, right, upstart);
236 
237 		VectorSet(offset,0,0,200); // scan for height above head
238 		G_ProjectSource (self->s.origin, offset, forward, right, upend);
239 		traceUp = gi.trace(upstart, NULL, NULL, upend, self, MASK_OPAQUE);
240 
241 		VectorSet(offset,200,0,200*traceUp.fraction-5); // set as high as possible
242 		G_ProjectSource (self->s.origin, offset, forward, right, upend);
243 		traceUp = gi.trace(upstart, NULL, NULL, upend, self, MASK_OPAQUE);
244 
245 		// If the upper trace is not open, we need to turn.
246 		if(traceUp.fraction != 1)
247 		{
248 			if(traceRight.fraction > traceLeft.fraction)
249 				self->s.angles[YAW] += (1.0 - traceLeft.fraction) * 45.0;
250 			else
251 				self->s.angles[YAW] += -(1.0 - traceRight.fraction) * 45.0;
252 
253 			ucmd->forwardmove = 400;
254 			return true;
255 		}
256 	}
257 
258 	return false;
259 }
260 
261 ///////////////////////////////////////////////////////////////////////
262 // Make the change in angles a little more gradual, not so snappy
263 // Subtle, but noticeable.
264 //
265 // Modified from the original id ChangeYaw code...
266 ///////////////////////////////////////////////////////////////////////
ACEMV_ChangeBotAngle(edict_t * ent)267 void ACEMV_ChangeBotAngle (edict_t *ent)
268 {
269 	float	ideal_yaw;
270 	float   ideal_pitch;
271 	float	current_yaw;
272 	float   current_pitch;
273 	float	move;
274 	float	speed;
275 	vec3_t  ideal_angle;
276 
277 	// Normalize the move angle first
278 	VectorNormalize(ent->move_vector);
279 
280 	current_yaw = anglemod(ent->s.angles[YAW]);
281 	current_pitch = anglemod(ent->s.angles[PITCH]);
282 
283 	vectoangles (ent->move_vector, ideal_angle);
284 
285 	ideal_yaw = anglemod(ideal_angle[YAW]);
286 	ideal_pitch = anglemod(ideal_angle[PITCH]);
287 
288 	// Yaw
289 	if (current_yaw != ideal_yaw)
290 	{
291 		move = ideal_yaw - current_yaw;
292 		speed = ent->yaw_speed;
293 		if (ideal_yaw > current_yaw)
294 		{
295 			if (move >= 180)
296 				move = move - 360;
297 		}
298 		else
299 		{
300 			if (move <= -180)
301 				move = move + 360;
302 		}
303 		if (move > 0)
304 		{
305 			if (move > speed)
306 				move = speed;
307 		}
308 		else
309 		{
310 			if (move < -speed)
311 				move = -speed;
312 		}
313 		ent->s.angles[YAW] = anglemod (current_yaw + move);
314 	}
315 
316 	// Pitch
317 	if (current_pitch != ideal_pitch)
318 	{
319 		move = ideal_pitch - current_pitch;
320 		speed = ent->yaw_speed;
321 		if (ideal_pitch > current_pitch)
322 		{
323 			if (move >= 180)
324 				move = move - 360;
325 		}
326 		else
327 		{
328 			if (move <= -180)
329 				move = move + 360;
330 		}
331 		if (move > 0)
332 		{
333 			if (move > speed)
334 				move = speed;
335 		}
336 		else
337 		{
338 			if (move < -speed)
339 				move = -speed;
340 		}
341 		ent->s.angles[PITCH] = anglemod (current_pitch + move);
342 	}
343 }
344 
345 ///////////////////////////////////////////////////////////////////////
346 // Set bot to move to it's movetarget. (following node path)
347 ///////////////////////////////////////////////////////////////////////
ACEMV_MoveToGoal(edict_t * self,usercmd_t * ucmd)348 void ACEMV_MoveToGoal(edict_t *self, usercmd_t *ucmd)
349 {
350 	// If a rocket or grenade is around deal with it
351 	// Simple, but effective (could be rewritten to be more accurate)
352 	if(strcmp(self->movetarget->classname,"rocket")==0 ||
353 	   strcmp(self->movetarget->classname,"grenade")==0)
354 	{
355 		VectorSubtract (self->movetarget->s.origin, self->s.origin, self->move_vector);
356 		ACEMV_ChangeBotAngle(self);
357 		if(debug_mode)
358 			debug_printf("%s: Oh crap a rocket!\n",self->client->pers.netname);
359 
360 		// strafe left/right
361 		if(rand()%1 && ACEMV_CanMove(self, MOVE_LEFT))
362 				ucmd->sidemove = -400;
363 		else if(ACEMV_CanMove(self, MOVE_RIGHT))
364 				ucmd->sidemove = 400;
365 		return;
366 
367 	}
368 	else
369 	{
370 		// Set bot's movement direction
371 		VectorSubtract (self->movetarget->s.origin, self->s.origin, self->move_vector);
372 		ACEMV_ChangeBotAngle(self);
373 		ucmd->forwardmove = 400;
374 		return;
375 	}
376 }
377 
378 ///////////////////////////////////////////////////////////////////////
379 // Main movement code. (following node path)
380 ///////////////////////////////////////////////////////////////////////
ACEMV_Move(edict_t * self,usercmd_t * ucmd)381 void ACEMV_Move(edict_t *self, usercmd_t *ucmd)
382 {
383 	vec3_t dist;
384 	int current_node_type=-1;
385 	int next_node_type=-1;
386 	int i;
387 
388 	// Get current and next node back from nav code.
389 	if(!ACEND_FollowPath(self))
390 	{
391 		self->state = STATE_WANDER;
392 		self->wander_timeout = level.time + 1.0;
393 		return;
394 	}
395 
396 	current_node_type = nodes[self->current_node].type;
397 	next_node_type = nodes[self->next_node].type;
398 
399 	///////////////////////////
400 	// Move To Goal
401 	///////////////////////////
402 	if (self->movetarget)
403 		ACEMV_MoveToGoal(self,ucmd);
404 
405 	////////////////////////////////////////////////////////
406 	// Grapple
407 	///////////////////////////////////////////////////////
408 	/*if(next_node_type == NODE_GRAPPLE)
409 	{
410 		ACEMV_ChangeBotAngle(self);
411 		ACEIT_ChangeWeapon(self,FindItem("grapple"));
412 		ucmd->buttons = BUTTON_ATTACK;
413 		return;
414 	}
415 	// Reset the grapple if hangin on a graple node
416 	if(current_node_type == NODE_GRAPPLE)
417 	{
418 		CTFPlayerResetGrapple(self);
419 		return;
420 	}
421 	*/
422 	////////////////////////////////////////////////////////
423 	// Platforms
424 	///////////////////////////////////////////////////////
425 	if(current_node_type != NODE_PLATFORM && next_node_type == NODE_PLATFORM)
426 	{
427 		// check to see if lift is down?
428 		for(i=0;i<num_items;i++)
429 			if(item_table[i].node == self->next_node)
430 				if(item_table[i].ent->moveinfo.state != STATE_BOTTOM)
431 				    return; // Wait for elevator
432 	}
433 	if(current_node_type == NODE_PLATFORM && next_node_type == NODE_PLATFORM)
434 	{
435 		// Move to the center
436 		self->move_vector[2] = 0; // kill z movement
437 		if(VectorLength(self->move_vector) > 10)
438 			ucmd->forwardmove = 200; // walk to center
439 
440 		ACEMV_ChangeBotAngle(self);
441 
442 		return; // No move, riding elevator
443 	}
444 
445 	////////////////////////////////////////////////////////
446 	// Jumpto Nodes
447 	///////////////////////////////////////////////////////
448 	if(next_node_type == NODE_JUMP ||
449 	  (current_node_type == NODE_JUMP && next_node_type != NODE_ITEM && nodes[self->next_node].origin[2] > self->s.origin[2]))
450 	{
451 		// Set up a jump move
452 		ucmd->forwardmove = 400;
453 		ucmd->upmove = 400;
454 
455 		ACEMV_ChangeBotAngle(self);
456 
457 		VectorCopy(self->move_vector,dist);
458 		VectorScale(dist,440,self->velocity);
459 
460 		return;
461 	}
462 
463 	////////////////////////////////////////////////////////
464 	// Ladder Nodes
465 	///////////////////////////////////////////////////////
466 	if(next_node_type == NODE_LADDER && nodes[self->next_node].origin[2] > self->s.origin[2])
467 	{
468 		// Otherwise move as fast as we can
469 		ucmd->forwardmove = 400;
470 		self->velocity[2] = 320;
471 
472 		ACEMV_ChangeBotAngle(self);
473 
474 		return;
475 
476 	}
477 	// If getting off the ladder
478 	if(current_node_type == NODE_LADDER && next_node_type != NODE_LADDER &&
479 	   nodes[self->next_node].origin[2] > self->s.origin[2])
480 	{
481 		ucmd->forwardmove = 400;
482 		ucmd->upmove = 200;
483 		self->velocity[2] = 200;
484 		ACEMV_ChangeBotAngle(self);
485 		return;
486 	}
487 
488 	////////////////////////////////////////////////////////
489 	// Water Nodes
490 	///////////////////////////////////////////////////////
491 	if(current_node_type == NODE_WATER)
492 	{
493 		// We need to be pointed up/down
494 		ACEMV_ChangeBotAngle(self);
495 
496 		// If the next node is not in the water, then move up to get out.
497 		if(next_node_type != NODE_WATER && !(gi.pointcontents(nodes[self->next_node].origin) & MASK_WATER)) // Exit water
498 			ucmd->upmove = 400;
499 
500 		ucmd->forwardmove = 300;
501 		return;
502 
503 	}
504 
505 	// Falling off ledge?
506 	if(!self->groundentity)
507 	{
508 		ACEMV_ChangeBotAngle(self);
509 
510 		self->velocity[0] = self->move_vector[0] * 360;
511 		self->velocity[1] = self->move_vector[1] * 360;
512 
513 		return;
514 	}
515 
516 	// Check to see if stuck, and if so try to free us
517 	// Also handles crouching
518  	if(VectorLength(self->velocity) < 37)
519 	{
520 		// Keep a random factor just in case....
521 		if(random() > 0.1 && ACEMV_SpecialMove(self, ucmd))
522 			return;
523 
524 		self->s.angles[YAW] += random() * 180 - 90;
525 
526 		ucmd->forwardmove = 400;
527 
528 		return;
529 	}
530 
531 	// Otherwise move as fast as we can
532 	ucmd->forwardmove = 400;
533 
534 	ACEMV_ChangeBotAngle(self);
535 
536 }
537 
538 
539 ///////////////////////////////////////////////////////////////////////
540 // Wandering code (based on old ACE movement code)
541 ///////////////////////////////////////////////////////////////////////
ACEMV_Wander(edict_t * self,usercmd_t * ucmd)542 void ACEMV_Wander(edict_t *self, usercmd_t *ucmd)
543 {
544 	vec3_t  temp;
545 
546 	// Do not move
547 	if(self->next_move_time > level.time)
548 		return;
549 
550 	// Special check for elevators, stand still until the ride comes to a complete stop.
551 	if(self->groundentity != NULL && self->groundentity->use == Use_Plat)
552 		if(self->groundentity->moveinfo.state == STATE_UP ||
553 		   self->groundentity->moveinfo.state == STATE_DOWN) // only move when platform not
554 		{
555 			self->velocity[0] = 0;
556 			self->velocity[1] = 0;
557 			self->velocity[2] = 0;
558 			self->next_move_time = level.time + 0.5;
559 			return;
560 		}
561 
562 
563 	// Is there a target to move to
564 	if (self->movetarget)
565 		ACEMV_MoveToGoal(self,ucmd);
566 
567 	////////////////////////////////
568 	// Swimming?
569 	////////////////////////////////
570 	VectorCopy(self->s.origin,temp);
571 	temp[2]+=24;
572 
573 	if(gi.pointcontents (temp) & MASK_WATER)
574 	{
575 		// If drowning and no node, move up
576 		if(self->client->next_drown_time > 0)
577 		{
578 			ucmd->upmove = 1;
579 			self->s.angles[PITCH] = -45;
580 		}
581 		else
582 			ucmd->upmove = 15;
583 
584 		ucmd->forwardmove = 300;
585 	}
586 	else
587 		self->client->next_drown_time = 0; // probably shound not be messing with this, but
588 
589 	////////////////////////////////
590 	// Lava?
591 	////////////////////////////////
592 	temp[2]-=48;
593 	if(gi.pointcontents(temp) & (CONTENTS_LAVA|CONTENTS_SLIME))
594 	{
595 		//	safe_bprintf(PRINT_MEDIUM,"lava jump\n");
596 		self->s.angles[YAW] += random() * 360 - 180;
597 		ucmd->forwardmove = 400;
598 		ucmd->upmove = 400;
599 		return;
600 	}
601 
602 	if(ACEMV_CheckEyes(self,ucmd))
603 		return;
604 
605 	// Check for special movement if we have a normal move (have to test)
606  	if(VectorLength(self->velocity) < 37)
607 	{
608 		if(random() > 0.1 && ACEMV_SpecialMove(self,ucmd))
609 			return;
610 
611 		self->s.angles[YAW] += random() * 180 - 90;
612 
613 		if(!M_CheckBottom(self) || !self->groundentity) // if there is ground continue otherwise wait for next move
614 			ucmd->forwardmove = 400;
615 
616 		return;
617 	}
618 
619 	ucmd->forwardmove = 400;
620 
621 }
622 
623 ///////////////////////////////////////////////////////////////////////
624 // Attack movement routine
625 //
626 // NOTE: Very simple for now, just a basic move about avoidance.
627 //       Change this routine for more advanced attack movement.
628 ///////////////////////////////////////////////////////////////////////
ACEMV_Attack(edict_t * self,usercmd_t * ucmd)629 void ACEMV_Attack (edict_t *self, usercmd_t *ucmd)
630 {
631 	float c;
632 	vec3_t  target;
633 	vec3_t  angles;
634 
635 	// Randomly choose a movement direction
636 	c = random();
637 
638 	if(c < 0.2 && ACEMV_CanMove(self,MOVE_LEFT))
639 		ucmd->sidemove -= 400;
640 	else if(c < 0.4 && ACEMV_CanMove(self,MOVE_RIGHT))
641 		ucmd->sidemove += 400;
642 
643 	if(c < 0.6 && ACEMV_CanMove(self,MOVE_FORWARD))
644 		ucmd->forwardmove += 400;
645 	else if(c < 0.8 && ACEMV_CanMove(self,MOVE_FORWARD))
646 		ucmd->forwardmove -= 400;
647 
648 	if(c < 0.95)
649 		ucmd->upmove -= 200;
650 	else
651 		ucmd->upmove += 200;
652 
653 	// Set the attack
654 	ucmd->buttons = BUTTON_ATTACK;
655 
656 	// Aim
657 	VectorCopy(self->enemy->s.origin,target);
658 
659 	// modify attack angles based on accuracy (mess this up to make the bot's aim not so deadly)
660 //	target[0] += (random()-0.5) * 20;
661 //	target[1] += (random()-0.5) * 20;
662 
663 	// Set direction
664 	VectorSubtract (target, self->s.origin, self->move_vector);
665 	vectoangles (self->move_vector, angles);
666 	VectorCopy(angles,self->s.angles);
667 
668 //	if(debug_mode)
669 //		debug_printf("%s attacking %s\n",self->client->pers.netname,self->enemy->client->pers.netname);
670 }
671 
672