2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // sv_move.c -- monster movement
23 #include "prvm_cmds.h"
29 Returns false if any part of the bottom of the entity is off an edge that
36 qboolean SV_CheckBottom (prvm_edict_t *ent)
38 vec3_t mins, maxs, start, stop;
43 VectorAdd (ent->fields.server->origin, ent->fields.server->mins, mins);
44 VectorAdd (ent->fields.server->origin, ent->fields.server->maxs, maxs);
46 // if all of the points under the corners are solid world, don't bother
47 // with the tougher checks
48 // the corners must be within 16 of the midpoint
49 start[2] = mins[2] - 1;
50 for (x=0 ; x<=1 ; x++)
51 for (y=0 ; y<=1 ; y++)
53 start[0] = x ? maxs[0] : mins[0];
54 start[1] = y ? maxs[1] : mins[1];
55 if (!(SV_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
60 return true; // we got out easy
65 // check it for real...
69 // the midpoint must be within 16 of the bottom
70 start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
71 start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
72 stop[2] = start[2] - 2*sv_stepheight.value;
73 trace = SV_TraceLine(start, stop, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
75 if (trace.fraction == 1.0)
77 mid = bottom = trace.endpos[2];
79 // the corners must be within 16 of the midpoint
80 for (x=0 ; x<=1 ; x++)
81 for (y=0 ; y<=1 ; y++)
83 start[0] = stop[0] = x ? maxs[0] : mins[0];
84 start[1] = stop[1] = y ? maxs[1] : mins[1];
86 trace = SV_TraceLine(start, stop, MOVE_NOMONSTERS, ent, SV_GenericHitSuperContentsMask(ent));
88 if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
89 bottom = trace.endpos[2];
90 if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
103 Called by monster program code.
104 The move will be adjusted for slopes and stairs, but if the move isn't
105 possible, no move is done and false is returned
108 qboolean SV_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink, qboolean noenemy, qboolean settrace)
111 vec3_t oldorg, neworg, end, traceendpos;
117 VectorCopy (ent->fields.server->origin, oldorg);
118 VectorAdd (ent->fields.server->origin, move, neworg);
120 // flying monsters don't step up
121 if ( (int)ent->fields.server->flags & (FL_SWIM | FL_FLY) )
123 // try one move with vertical motion, then one without
124 for (i=0 ; i<2 ; i++)
126 VectorAdd (ent->fields.server->origin, move, neworg);
128 enemy = prog->edicts;
131 enemy = PRVM_PROG_TO_EDICT(ent->fields.server->enemy);
132 if (i == 0 && enemy != prog->edicts)
134 dz = ent->fields.server->origin[2] - PRVM_PROG_TO_EDICT(ent->fields.server->enemy)->fields.server->origin[2];
141 trace = SV_TraceBox(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, neworg, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
143 if (trace.fraction == 1)
145 VectorCopy(trace.endpos, traceendpos);
146 if (((int)ent->fields.server->flags & FL_SWIM) && !(SV_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
147 return false; // swim monster left water
149 VectorCopy (traceendpos, ent->fields.server->origin);
153 SV_LinkEdict_TouchAreaGrid(ent);
158 if (enemy == prog->edicts)
165 // push down from a step height above the wished position
166 neworg[2] += sv_stepheight.value;
167 VectorCopy (neworg, end);
168 end[2] -= sv_stepheight.value*2;
170 trace = SV_TraceBox(neworg, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
172 if (trace.startsolid)
174 neworg[2] -= sv_stepheight.value;
175 trace = SV_TraceBox(neworg, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
176 if (trace.startsolid)
179 if (trace.fraction == 1)
181 // if monster had the ground pulled out, go ahead and fall
182 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
184 VectorAdd (ent->fields.server->origin, move, ent->fields.server->origin);
188 SV_LinkEdict_TouchAreaGrid(ent);
190 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
194 return false; // walked off an edge
197 // check point traces down for dangling corners
198 VectorCopy (trace.endpos, ent->fields.server->origin);
200 if (!SV_CheckBottom (ent))
202 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
203 { // entity had floor mostly pulled out from underneath it
204 // and is trying to correct
208 SV_LinkEdict_TouchAreaGrid(ent);
212 VectorCopy (oldorg, ent->fields.server->origin);
216 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
217 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_PARTIALGROUND;
219 // gameplayfix: check if reached pretty steep plane and bail
220 if ( ! ( (int)ent->fields.server->flags & (FL_SWIM | FL_FLY) ) && sv_gameplayfix_nostepmoveonsteepslopes.integer )
222 if (trace.plane.normal[ 2 ] < 0.5)
224 VectorCopy (oldorg, ent->fields.server->origin);
229 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
235 SV_LinkEdict_TouchAreaGrid(ent);
241 //============================================================================
244 ======================
247 Turns to the movement direction, and walks the current distance if
250 ======================
252 void VM_changeyaw (void);
253 qboolean SV_StepDirection (prvm_edict_t *ent, float yaw, float dist)
255 vec3_t move, oldorigin;
258 ent->fields.server->ideal_yaw = yaw;
261 yaw = yaw*M_PI*2 / 360;
262 move[0] = cos(yaw)*dist;
263 move[1] = sin(yaw)*dist;
266 VectorCopy (ent->fields.server->origin, oldorigin);
267 if (SV_movestep (ent, move, false, false, false))
269 delta = ent->fields.server->angles[YAW] - ent->fields.server->ideal_yaw;
270 if (delta > 45 && delta < 315)
271 { // not turned far enough, so don't take the step
272 VectorCopy (oldorigin, ent->fields.server->origin);
275 SV_LinkEdict_TouchAreaGrid(ent);
279 SV_LinkEdict_TouchAreaGrid(ent);
285 ======================
288 ======================
290 void SV_FixCheckBottom (prvm_edict_t *ent)
292 ent->fields.server->flags = (int)ent->fields.server->flags | FL_PARTIALGROUND;
304 void SV_NewChaseDir (prvm_edict_t *actor, prvm_edict_t *enemy, float dist)
308 float tdir, olddir, turnaround;
310 olddir = ANGLEMOD((int)(actor->fields.server->ideal_yaw/45)*45);
311 turnaround = ANGLEMOD(olddir - 180);
313 deltax = enemy->fields.server->origin[0] - actor->fields.server->origin[0];
314 deltay = enemy->fields.server->origin[1] - actor->fields.server->origin[1];
329 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
332 tdir = d[2] == 90 ? 45 : 315;
334 tdir = d[2] == 90 ? 135 : 215;
336 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
340 // try other directions
341 if ( ((rand()&3) & 1) || fabs(deltay)>fabs(deltax))
348 if (d[1]!=DI_NODIR && d[1]!=turnaround
349 && SV_StepDirection(actor, d[1], dist))
352 if (d[2]!=DI_NODIR && d[2]!=turnaround
353 && SV_StepDirection(actor, d[2], dist))
356 /* there is no direct path to the player, so pick another direction */
358 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
361 if (rand()&1) /*randomly determine direction of search*/
363 for (tdir=0 ; tdir<=315 ; tdir += 45)
364 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
369 for (tdir=315 ; tdir >=0 ; tdir -= 45)
370 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
374 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
377 actor->fields.server->ideal_yaw = olddir; // can't move
379 // if a bridge was pulled out from underneath a monster, it may not have
380 // a valid standing position at all
382 if (!SV_CheckBottom (actor))
383 SV_FixCheckBottom (actor);
388 ======================
391 ======================
393 qboolean SV_CloseEnough (prvm_edict_t *ent, prvm_edict_t *goal, float dist)
397 for (i=0 ; i<3 ; i++)
399 if (goal->priv.server->areamins[i] > ent->priv.server->areamaxs[i] + dist)
401 if (goal->priv.server->areamaxs[i] < ent->priv.server->areamins[i] - dist)
408 ======================
411 ======================
413 void SV_MoveToGoal (void)
415 prvm_edict_t *ent, *goal;
418 VM_SAFEPARMCOUNT(1, SV_MoveToGoal);
420 ent = PRVM_PROG_TO_EDICT(prog->globals.server->self);
421 goal = PRVM_PROG_TO_EDICT(ent->fields.server->goalentity);
422 dist = PRVM_G_FLOAT(OFS_PARM0);
424 if ( !( (int)ent->fields.server->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
426 PRVM_G_FLOAT(OFS_RETURN) = 0;
430 // if the next step hits the enemy, return immediately
431 if ( PRVM_PROG_TO_EDICT(ent->fields.server->enemy) != prog->edicts && SV_CloseEnough (ent, goal, dist) )
435 if ( (rand()&3)==1 ||
436 !SV_StepDirection (ent, ent->fields.server->ideal_yaw, dist))
438 SV_NewChaseDir (ent, goal, dist);