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 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
225 SV_LinkEdict_TouchAreaGrid(ent);
231 //============================================================================
234 ======================
237 Turns to the movement direction, and walks the current distance if
240 ======================
242 void VM_changeyaw (void);
243 qboolean SV_StepDirection (prvm_edict_t *ent, float yaw, float dist)
245 vec3_t move, oldorigin;
248 ent->fields.server->ideal_yaw = yaw;
251 yaw = yaw*M_PI*2 / 360;
252 move[0] = cos(yaw)*dist;
253 move[1] = sin(yaw)*dist;
256 VectorCopy (ent->fields.server->origin, oldorigin);
257 if (SV_movestep (ent, move, false, false, false))
259 delta = ent->fields.server->angles[YAW] - ent->fields.server->ideal_yaw;
260 if (delta > 45 && delta < 315)
261 { // not turned far enough, so don't take the step
262 VectorCopy (oldorigin, ent->fields.server->origin);
265 SV_LinkEdict_TouchAreaGrid(ent);
269 SV_LinkEdict_TouchAreaGrid(ent);
275 ======================
278 ======================
280 void SV_FixCheckBottom (prvm_edict_t *ent)
282 ent->fields.server->flags = (int)ent->fields.server->flags | FL_PARTIALGROUND;
294 void SV_NewChaseDir (prvm_edict_t *actor, prvm_edict_t *enemy, float dist)
298 float tdir, olddir, turnaround;
300 olddir = ANGLEMOD((int)(actor->fields.server->ideal_yaw/45)*45);
301 turnaround = ANGLEMOD(olddir - 180);
303 deltax = enemy->fields.server->origin[0] - actor->fields.server->origin[0];
304 deltay = enemy->fields.server->origin[1] - actor->fields.server->origin[1];
319 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
322 tdir = d[2] == 90 ? 45 : 315;
324 tdir = d[2] == 90 ? 135 : 215;
326 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
330 // try other directions
331 if ( ((rand()&3) & 1) || fabs(deltay)>fabs(deltax))
338 if (d[1]!=DI_NODIR && d[1]!=turnaround
339 && SV_StepDirection(actor, d[1], dist))
342 if (d[2]!=DI_NODIR && d[2]!=turnaround
343 && SV_StepDirection(actor, d[2], dist))
346 /* there is no direct path to the player, so pick another direction */
348 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
351 if (rand()&1) /*randomly determine direction of search*/
353 for (tdir=0 ; tdir<=315 ; tdir += 45)
354 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
359 for (tdir=315 ; tdir >=0 ; tdir -= 45)
360 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
364 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
367 actor->fields.server->ideal_yaw = olddir; // can't move
369 // if a bridge was pulled out from underneath a monster, it may not have
370 // a valid standing position at all
372 if (!SV_CheckBottom (actor))
373 SV_FixCheckBottom (actor);
378 ======================
381 ======================
383 qboolean SV_CloseEnough (prvm_edict_t *ent, prvm_edict_t *goal, float dist)
387 for (i=0 ; i<3 ; i++)
389 if (goal->priv.server->areamins[i] > ent->priv.server->areamaxs[i] + dist)
391 if (goal->priv.server->areamaxs[i] < ent->priv.server->areamins[i] - dist)
398 ======================
401 ======================
403 void SV_MoveToGoal (void)
405 prvm_edict_t *ent, *goal;
408 VM_SAFEPARMCOUNT(1, SV_MoveToGoal);
410 ent = PRVM_PROG_TO_EDICT(prog->globals.server->self);
411 goal = PRVM_PROG_TO_EDICT(ent->fields.server->goalentity);
412 dist = PRVM_G_FLOAT(OFS_PARM0);
414 if ( !( (int)ent->fields.server->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
416 PRVM_G_FLOAT(OFS_RETURN) = 0;
420 // if the next step hits the enemy, return immediately
421 if ( PRVM_PROG_TO_EDICT(ent->fields.server->enemy) != prog->edicts && SV_CloseEnough (ent, goal, dist) )
425 if ( (rand()&3)==1 ||
426 !SV_StepDirection (ent, ent->fields.server->ideal_yaw, dist))
428 SV_NewChaseDir (ent, goal, dist);