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_Move (start, vec3_origin, vec3_origin, 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_Move (start, vec3_origin, vec3_origin, 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);
129 enemy = PRVM_PROG_TO_EDICT(ent->fields.server->enemy);
130 if (i == 0 && enemy != prog->edicts)
132 dz = ent->fields.server->origin[2] - PRVM_PROG_TO_EDICT(ent->fields.server->enemy)->fields.server->origin[2];
139 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, neworg, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
141 if (trace.fraction == 1)
143 VectorCopy(trace.endpos, traceendpos);
144 if (((int)ent->fields.server->flags & FL_SWIM) && !(SV_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
145 return false; // swim monster left water
147 VectorCopy (traceendpos, ent->fields.server->origin);
149 SV_LinkEdict (ent, true);
153 if (enemy == prog->edicts)
160 // push down from a step height above the wished position
161 neworg[2] += sv_stepheight.value;
162 VectorCopy (neworg, end);
163 end[2] -= sv_stepheight.value*2;
165 trace = SV_Move (neworg, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
167 if (trace.startsolid)
169 neworg[2] -= sv_stepheight.value;
170 trace = SV_Move (neworg, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, SV_GenericHitSuperContentsMask(ent));
171 if (trace.startsolid)
174 if (trace.fraction == 1)
176 // if monster had the ground pulled out, go ahead and fall
177 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
179 VectorAdd (ent->fields.server->origin, move, ent->fields.server->origin);
181 SV_LinkEdict (ent, true);
182 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
186 return false; // walked off an edge
189 // check point traces down for dangling corners
190 VectorCopy (trace.endpos, ent->fields.server->origin);
192 if (!SV_CheckBottom (ent))
194 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
195 { // entity had floor mostly pulled out from underneath it
196 // and is trying to correct
198 SV_LinkEdict (ent, true);
201 VectorCopy (oldorg, ent->fields.server->origin);
205 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
206 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_PARTIALGROUND;
208 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
212 SV_LinkEdict (ent, true);
217 //============================================================================
220 ======================
223 Turns to the movement direction, and walks the current distance if
226 ======================
228 void VM_changeyaw (void);
229 qboolean SV_StepDirection (prvm_edict_t *ent, float yaw, float dist)
231 vec3_t move, oldorigin;
234 ent->fields.server->ideal_yaw = yaw;
237 yaw = yaw*M_PI*2 / 360;
238 move[0] = cos(yaw)*dist;
239 move[1] = sin(yaw)*dist;
242 VectorCopy (ent->fields.server->origin, oldorigin);
243 if (SV_movestep (ent, move, false, false, false))
245 delta = ent->fields.server->angles[YAW] - ent->fields.server->ideal_yaw;
246 if (delta > 45 && delta < 315)
247 { // not turned far enough, so don't take the step
248 VectorCopy (oldorigin, ent->fields.server->origin);
250 SV_LinkEdict (ent, true);
253 SV_LinkEdict (ent, true);
259 ======================
262 ======================
264 void SV_FixCheckBottom (prvm_edict_t *ent)
266 ent->fields.server->flags = (int)ent->fields.server->flags | FL_PARTIALGROUND;
278 void SV_NewChaseDir (prvm_edict_t *actor, prvm_edict_t *enemy, float dist)
282 float tdir, olddir, turnaround;
284 olddir = ANGLEMOD((int)(actor->fields.server->ideal_yaw/45)*45);
285 turnaround = ANGLEMOD(olddir - 180);
287 deltax = enemy->fields.server->origin[0] - actor->fields.server->origin[0];
288 deltay = enemy->fields.server->origin[1] - actor->fields.server->origin[1];
303 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
306 tdir = d[2] == 90 ? 45 : 315;
308 tdir = d[2] == 90 ? 135 : 215;
310 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
314 // try other directions
315 if ( ((rand()&3) & 1) || fabs(deltay)>fabs(deltax))
322 if (d[1]!=DI_NODIR && d[1]!=turnaround
323 && SV_StepDirection(actor, d[1], dist))
326 if (d[2]!=DI_NODIR && d[2]!=turnaround
327 && SV_StepDirection(actor, d[2], dist))
330 /* there is no direct path to the player, so pick another direction */
332 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
335 if (rand()&1) /*randomly determine direction of search*/
337 for (tdir=0 ; tdir<=315 ; tdir += 45)
338 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
343 for (tdir=315 ; tdir >=0 ; tdir -= 45)
344 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
348 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
351 actor->fields.server->ideal_yaw = olddir; // can't move
353 // if a bridge was pulled out from underneath a monster, it may not have
354 // a valid standing position at all
356 if (!SV_CheckBottom (actor))
357 SV_FixCheckBottom (actor);
362 ======================
365 ======================
367 qboolean SV_CloseEnough (prvm_edict_t *ent, prvm_edict_t *goal, float dist)
371 for (i=0 ; i<3 ; i++)
373 if (goal->priv.server->areamins[i] > ent->priv.server->areamaxs[i] + dist)
375 if (goal->priv.server->areamaxs[i] < ent->priv.server->areamins[i] - dist)
382 ======================
385 ======================
387 void SV_MoveToGoal (void)
389 prvm_edict_t *ent, *goal;
392 VM_SAFEPARMCOUNT(1, SV_MoveToGoal);
394 ent = PRVM_PROG_TO_EDICT(prog->globals.server->self);
395 goal = PRVM_PROG_TO_EDICT(ent->fields.server->goalentity);
396 dist = PRVM_G_FLOAT(OFS_PARM0);
398 if ( !( (int)ent->fields.server->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
400 PRVM_G_FLOAT(OFS_RETURN) = 0;
404 // if the next step hits the enemy, return immediately
405 if ( PRVM_PROG_TO_EDICT(ent->fields.server->enemy) != prog->edicts && SV_CloseEnough (ent, goal, dist) )
409 if ( (rand()&3)==1 ||
410 !SV_StepDirection (ent, ent->fields.server->ideal_yaw, dist))
412 SV_NewChaseDir (ent, goal, dist);