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