vkQuake2 doxygen  1.0 dev
m_move.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 
19 */
20 // m_move.c -- monster movement
21 
22 #include "g_local.h"
23 
24 #define STEPSIZE 18
25 
26 /*
27 =============
28 M_CheckBottom
29 
30 Returns false if any part of the bottom of the entity is off an edge that
31 is not a staircase.
32 
33 =============
34 */
35 int c_yes, c_no;
36 
38 {
39  vec3_t mins, maxs, start, stop;
40  trace_t trace;
41  int x, y;
42  float mid, bottom;
43 
44  VectorAdd (ent->s.origin, ent->mins, mins);
45  VectorAdd (ent->s.origin, ent->maxs, maxs);
46 
47 // if all of the points under the corners are solid world, don't bother
48 // with the tougher checks
49 // the corners must be within 16 of the midpoint
50  start[2] = mins[2] - 1;
51  for (x=0 ; x<=1 ; x++)
52  for (y=0 ; y<=1 ; y++)
53  {
54  start[0] = x ? maxs[0] : mins[0];
55  start[1] = y ? maxs[1] : mins[1];
56  if (gi.pointcontents (start) != CONTENTS_SOLID)
57  goto realcheck;
58  }
59 
60  c_yes++;
61  return true; // we got out easy
62 
63 realcheck:
64  c_no++;
65 //
66 // check it for real...
67 //
68  start[2] = mins[2];
69 
70 // the midpoint must be within 16 of the bottom
71  start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
72  start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
73  stop[2] = start[2] - 2*STEPSIZE;
74  trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
75 
76  if (trace.fraction == 1.0)
77  return false;
78  mid = bottom = trace.endpos[2];
79 
80 // the corners must be within 16 of the midpoint
81  for (x=0 ; x<=1 ; x++)
82  for (y=0 ; y<=1 ; y++)
83  {
84  start[0] = stop[0] = x ? maxs[0] : mins[0];
85  start[1] = stop[1] = y ? maxs[1] : mins[1];
86 
87  trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
88 
89  if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
90  bottom = trace.endpos[2];
91  if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
92  return false;
93  }
94 
95  c_yes++;
96  return true;
97 }
98 
99 
100 /*
101 =============
102 SV_movestep
103 
104 Called by monster program code.
105 The move will be adjusted for slopes and stairs, but if the move isn't
106 possible, no move is done, false is returned, and
107 pr_global_struct->trace_normal is set to the normal of the blocking wall
108 =============
109 */
110 //FIXME since we need to test end position contents here, can we avoid doing
111 //it again later in catagorize position?
113 {
114  float dz;
115  vec3_t oldorg, neworg, end;
116  trace_t trace;
117  int i;
118  float stepsize;
119  vec3_t test;
120  int contents;
121 
122 // try the move
123  VectorCopy (ent->s.origin, oldorg);
124  VectorAdd (ent->s.origin, move, neworg);
125 
126 // flying monsters don't step up
127  if ( ent->flags & (FL_SWIM | FL_FLY) )
128  {
129  // try one move with vertical motion, then one without
130  for (i=0 ; i<2 ; i++)
131  {
132  VectorAdd (ent->s.origin, move, neworg);
133  if (i == 0 && ent->enemy)
134  {
135  if (!ent->goalentity)
136  ent->goalentity = ent->enemy;
137  dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
138  if (ent->goalentity->client)
139  {
140  if (dz > 40)
141  neworg[2] -= 8;
142  if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
143  if (dz < 30)
144  neworg[2] += 8;
145  }
146  else
147  {
148  if (dz > 8)
149  neworg[2] -= 8;
150  else if (dz > 0)
151  neworg[2] -= dz;
152  else if (dz < -8)
153  neworg[2] += 8;
154  else
155  neworg[2] += dz;
156  }
157  }
158  trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
159 
160  // fly monsters don't enter water voluntarily
161  if (ent->flags & FL_FLY)
162  {
163  if (!ent->waterlevel)
164  {
165  test[0] = trace.endpos[0];
166  test[1] = trace.endpos[1];
167  test[2] = trace.endpos[2] + ent->mins[2] + 1;
168  contents = gi.pointcontents(test);
169  if (contents & MASK_WATER)
170  return false;
171  }
172  }
173 
174  // swim monsters don't exit water voluntarily
175  if (ent->flags & FL_SWIM)
176  {
177  if (ent->waterlevel < 2)
178  {
179  test[0] = trace.endpos[0];
180  test[1] = trace.endpos[1];
181  test[2] = trace.endpos[2] + ent->mins[2] + 1;
182  contents = gi.pointcontents(test);
183  if (!(contents & MASK_WATER))
184  return false;
185  }
186  }
187 
188  if (trace.fraction == 1)
189  {
190  VectorCopy (trace.endpos, ent->s.origin);
191  if (relink)
192  {
193  gi.linkentity (ent);
194  G_TouchTriggers (ent);
195  }
196  return true;
197  }
198 
199  if (!ent->enemy)
200  break;
201  }
202 
203  return false;
204  }
205 
206 // push down from a step height above the wished position
207  if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
208  stepsize = STEPSIZE;
209  else
210  stepsize = 1;
211 
212  neworg[2] += stepsize;
213  VectorCopy (neworg, end);
214  end[2] -= stepsize*2;
215 
216  trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
217 
218  if (trace.allsolid)
219  return false;
220 
221  if (trace.startsolid)
222  {
223  neworg[2] -= stepsize;
224  trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
225  if (trace.allsolid || trace.startsolid)
226  return false;
227  }
228 
229 
230  // don't go in to water
231  if (ent->waterlevel == 0)
232  {
233  test[0] = trace.endpos[0];
234  test[1] = trace.endpos[1];
235  test[2] = trace.endpos[2] + ent->mins[2] + 1;
236  contents = gi.pointcontents(test);
237 
238  if (contents & MASK_WATER)
239  return false;
240  }
241 
242  if (trace.fraction == 1)
243  {
244  // if monster had the ground pulled out, go ahead and fall
245  if ( ent->flags & FL_PARTIALGROUND )
246  {
247  VectorAdd (ent->s.origin, move, ent->s.origin);
248  if (relink)
249  {
250  gi.linkentity (ent);
251  G_TouchTriggers (ent);
252  }
253  ent->groundentity = NULL;
254  return true;
255  }
256 
257  return false; // walked off an edge
258  }
259 
260 // check point traces down for dangling corners
261  VectorCopy (trace.endpos, ent->s.origin);
262 
263  if (!M_CheckBottom (ent))
264  {
265  if ( ent->flags & FL_PARTIALGROUND )
266  { // entity had floor mostly pulled out from underneath it
267  // and is trying to correct
268  if (relink)
269  {
270  gi.linkentity (ent);
271  G_TouchTriggers (ent);
272  }
273  return true;
274  }
275  VectorCopy (oldorg, ent->s.origin);
276  return false;
277  }
278 
279  if ( ent->flags & FL_PARTIALGROUND )
280  {
281  ent->flags &= ~FL_PARTIALGROUND;
282  }
283  ent->groundentity = trace.ent;
284  ent->groundentity_linkcount = trace.ent->linkcount;
285 
286 // the move is ok
287  if (relink)
288  {
289  gi.linkentity (ent);
290  G_TouchTriggers (ent);
291  }
292  return true;
293 }
294 
295 
296 //============================================================================
297 
298 /*
299 ===============
300 M_ChangeYaw
301 
302 ===============
303 */
304 void M_ChangeYaw (edict_t *ent)
305 {
306  float ideal;
307  float current;
308  float move;
309  float speed;
310 
311  current = anglemod(ent->s.angles[YAW]);
312  ideal = ent->ideal_yaw;
313 
314  if (current == ideal)
315  return;
316 
317  move = ideal - current;
318  speed = ent->yaw_speed;
319  if (ideal > current)
320  {
321  if (move >= 180)
322  move = move - 360;
323  }
324  else
325  {
326  if (move <= -180)
327  move = move + 360;
328  }
329  if (move > 0)
330  {
331  if (move > speed)
332  move = speed;
333  }
334  else
335  {
336  if (move < -speed)
337  move = -speed;
338  }
339 
340  ent->s.angles[YAW] = anglemod (current + move);
341 }
342 
343 
344 /*
345 ======================
346 SV_StepDirection
347 
348 Turns to the movement direction, and walks the current distance if
349 facing it.
350 
351 ======================
352 */
353 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
354 {
355  vec3_t move, oldorigin;
356  float delta;
357 
358  ent->ideal_yaw = yaw;
359  M_ChangeYaw (ent);
360 
361  yaw = yaw*M_PI*2 / 360;
362  move[0] = cos(yaw)*dist;
363  move[1] = sin(yaw)*dist;
364  move[2] = 0;
365 
366  VectorCopy (ent->s.origin, oldorigin);
367  if (SV_movestep (ent, move, false))
368  {
369  delta = ent->s.angles[YAW] - ent->ideal_yaw;
370  if (delta > 45 && delta < 315)
371  { // not turned far enough, so don't take the step
372  VectorCopy (oldorigin, ent->s.origin);
373  }
374  gi.linkentity (ent);
375  G_TouchTriggers (ent);
376  return true;
377  }
378  gi.linkentity (ent);
379  G_TouchTriggers (ent);
380  return false;
381 }
382 
383 /*
384 ======================
385 SV_FixCheckBottom
386 
387 ======================
388 */
390 {
391  ent->flags |= FL_PARTIALGROUND;
392 }
393 
394 
395 
396 /*
397 ================
398 SV_NewChaseDir
399 
400 ================
401 */
402 #define DI_NODIR -1
403 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
404 {
405  float deltax,deltay;
406  float d[3];
407  float tdir, olddir, turnaround;
408 
409  //FIXME: how did we get here with no enemy
410  if (!enemy)
411  return;
412 
413  olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
414  turnaround = anglemod(olddir - 180);
415 
416  deltax = enemy->s.origin[0] - actor->s.origin[0];
417  deltay = enemy->s.origin[1] - actor->s.origin[1];
418  if (deltax>10)
419  d[1]= 0;
420  else if (deltax<-10)
421  d[1]= 180;
422  else
423  d[1]= DI_NODIR;
424  if (deltay<-10)
425  d[2]= 270;
426  else if (deltay>10)
427  d[2]= 90;
428  else
429  d[2]= DI_NODIR;
430 
431 // try direct route
432  if (d[1] != DI_NODIR && d[2] != DI_NODIR)
433  {
434  if (d[1] == 0)
435  tdir = d[2] == 90 ? 45 : 315;
436  else
437  tdir = d[2] == 90 ? 135 : 215;
438 
439  if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
440  return;
441  }
442 
443 // try other directions
444  if ( ((rand()&3) & 1) || fabs(deltay)>fabs(deltax))
445  {
446  tdir=d[1];
447  d[1]=d[2];
448  d[2]=tdir;
449  }
450 
451  if (d[1]!=DI_NODIR && d[1]!=turnaround
452  && SV_StepDirection(actor, d[1], dist))
453  return;
454 
455  if (d[2]!=DI_NODIR && d[2]!=turnaround
456  && SV_StepDirection(actor, d[2], dist))
457  return;
458 
459 /* there is no direct path to the player, so pick another direction */
460 
461  if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
462  return;
463 
464  if (rand()&1) /*randomly determine direction of search*/
465  {
466  for (tdir=0 ; tdir<=315 ; tdir += 45)
467  if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
468  return;
469  }
470  else
471  {
472  for (tdir=315 ; tdir >=0 ; tdir -= 45)
473  if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
474  return;
475  }
476 
477  if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
478  return;
479 
480  actor->ideal_yaw = olddir; // can't move
481 
482 // if a bridge was pulled out from underneath a monster, it may not have
483 // a valid standing position at all
484 
485  if (!M_CheckBottom (actor))
486  SV_FixCheckBottom (actor);
487 }
488 
489 /*
490 ======================
491 SV_CloseEnough
492 
493 ======================
494 */
495 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
496 {
497  int i;
498 
499  for (i=0 ; i<3 ; i++)
500  {
501  if (goal->absmin[i] > ent->absmax[i] + dist)
502  return false;
503  if (goal->absmax[i] < ent->absmin[i] - dist)
504  return false;
505  }
506  return true;
507 }
508 
509 
510 /*
511 ======================
512 M_MoveToGoal
513 ======================
514 */
515 void M_MoveToGoal (edict_t *ent, float dist)
516 {
517  edict_t *goal;
518 
519  goal = ent->goalentity;
520 
521  if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
522  return;
523 
524 // if the next step hits the enemy, return immediately
525  if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) )
526  return;
527 
528 // bump around...
529  if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
530  {
531  if (ent->inuse)
532  SV_NewChaseDir (ent, goal, dist);
533  }
534 }
535 
536 
537 /*
538 ===============
539 M_walkmove
540 ===============
541 */
542 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
543 {
544  vec3_t move;
545 
546  if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
547  return false;
548 
549  yaw = yaw*M_PI*2 / 360;
550 
551  move[0] = cos(yaw)*dist;
552  move[1] = sin(yaw)*dist;
553  move[2] = 0;
554 
555  return SV_movestep(ent, move, true);
556 }
gi
game_import_t gi
Definition: g_main.c:25
SV_CloseEnough
qboolean SV_CloseEnough(edict_t *ent, edict_t *goal, float dist)
Definition: m_move.c:495
edict_s::ideal_yaw
float ideal_yaw
Definition: g_local.h:1040
edict_s::s
entity_state_t s
Definition: g_local.h:970
edict_s::groundentity
edict_t * groundentity
Definition: g_local.h:1079
YAW
#define YAW
Definition: q_shared.h:73
MASK_MONSTERSOLID
#define MASK_MONSTERSOLID
Definition: q_shared.h:398
game_import_t::trace
trace_t(* trace)(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passent, int contentmask)
Definition: game.h:128
c_no
int c_no
Definition: m_move.c:35
trace_t::fraction
float fraction
Definition: q_shared.h:457
edict_s::absmax
vec3_t absmax
Definition: g_local.h:991
bottom
GLdouble GLdouble bottom
Definition: qgl_win.c:159
M_ChangeYaw
void M_ChangeYaw(edict_t *ent)
Definition: m_move.c:304
entity_state_s::origin
vec3_t origin
Definition: q_shared.h:1149
edict_s::linkcount
int linkcount
Definition: g_local.h:977
MASK_WATER
#define MASK_WATER
Definition: q_shared.h:399
qboolean
qboolean
Definition: q_shared.h:63
x
GLint GLenum GLint x
Definition: qgl_win.c:116
edict_s::inuse
qboolean inuse
Definition: g_local.h:976
trace_t
Definition: q_shared.h:453
i
int i
Definition: q_shared.c:305
G_TouchTriggers
void G_TouchTriggers(edict_t *ent)
Definition: g_utils.c:475
edict_s::client
struct gclient_s * client
Definition: g_local.h:971
CONTENTS_SOLID
#define CONTENTS_SOLID
Definition: qfiles.h:333
edict_s::mins
vec3_t mins
Definition: g_local.h:990
M_PI
#define M_PI
Definition: q_shared.h:142
edict_s::groundentity_linkcount
int groundentity_linkcount
Definition: g_local.h:1080
anglemod
float anglemod(float a)
Definition: q_shared.c:293
FL_SWIM
#define FL_SWIM
Definition: g_local.h:60
trace_t::allsolid
qboolean allsolid
Definition: q_shared.h:455
M_walkmove
qboolean M_walkmove(edict_t *ent, float yaw, float dist)
Definition: m_move.c:542
edict_s::speed
float speed
Definition: g_local.h:1026
edict_s
Definition: g_local.h:968
game_import_t::pointcontents
int(* pointcontents)(vec3_t point)
Definition: game.h:129
FL_PARTIALGROUND
#define FL_PARTIALGROUND
Definition: g_local.h:67
NULL
#define NULL
Definition: q_shared.h:67
current
static int current
Definition: cl_scrn.c:129
monsterinfo_t::aiflags
int aiflags
Definition: g_local.h:421
M_CheckBottom
qboolean M_CheckBottom(edict_t *ent)
Definition: m_move.c:37
edict_s::goalentity
edict_t * goalentity
Definition: g_local.h:1037
VectorAdd
#define VectorAdd(a, b, c)
Definition: q_shared.h:164
y
GLint y
Definition: qgl_win.c:115
DI_NODIR
#define DI_NODIR
Definition: m_move.c:402
edict_s::yaw_speed
float yaw_speed
Definition: g_local.h:1039
VectorCopy
#define VectorCopy(a, b)
Definition: q_shared.h:165
SV_FixCheckBottom
void SV_FixCheckBottom(edict_t *ent)
Definition: m_move.c:389
edict_s::monsterinfo
monsterinfo_t monsterinfo
Definition: g_local.h:1114
trace_t::endpos
vec3_t endpos
Definition: q_shared.h:458
vec3_origin
vec3_t vec3_origin
Definition: q_shared.c:24
M_MoveToGoal
void M_MoveToGoal(edict_t *ent, float dist)
Definition: m_move.c:515
SV_movestep
qboolean SV_movestep(edict_t *ent, vec3_t move, qboolean relink)
Definition: m_move.c:112
c_yes
int c_yes
Definition: m_move.c:35
SV_NewChaseDir
void SV_NewChaseDir(edict_t *actor, edict_t *enemy, float dist)
Definition: m_move.c:403
edict_s::flags
int flags
Definition: g_local.h:1002
edict_s::maxs
vec3_t maxs
Definition: g_local.h:990
trace_t::startsolid
qboolean startsolid
Definition: q_shared.h:456
trace_t::ent
struct edict_s * ent
Definition: q_shared.h:462
SV_StepDirection
qboolean SV_StepDirection(edict_t *ent, float yaw, float dist)
Definition: m_move.c:353
FL_FLY
#define FL_FLY
Definition: g_local.h:59
STEPSIZE
#define STEPSIZE
Definition: m_move.c:24
edict_s::waterlevel
int waterlevel
Definition: g_local.h:1100
edict_s::enemy
edict_t * enemy
Definition: g_local.h:1076
game_import_t::linkentity
void(* linkentity)(edict_t *ent)
Definition: game.h:138
entity_state_s::angles
vec3_t angles
Definition: q_shared.h:1150
vec3_t
vec_t vec3_t[3]
Definition: q_shared.h:134
edict_s::absmin
vec3_t absmin
Definition: g_local.h:991
g_local.h
AI_NOSTEP
#define AI_NOSTEP
Definition: g_local.h:139