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