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
28 Returns false if any part of the bottom of the entity is off an edge that
35 qboolean SV_CheckBottom (prvm_edict_t *ent)
37 vec3_t mins, maxs, start, stop;
42 VectorAdd (ent->fields.server->origin, ent->fields.server->mins, mins);
43 VectorAdd (ent->fields.server->origin, ent->fields.server->maxs, maxs);
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++)
52 start[0] = x ? maxs[0] : mins[0];
53 start[1] = y ? maxs[1] : mins[1];
54 if (!(SV_PointSuperContents(start) & (SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY)))
59 return true; // we got out easy
64 // check it for real...
68 // the midpoint must be within 16 of the bottom
69 start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
70 start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
71 stop[2] = start[2] - 2*sv_stepheight.value;
72 trace = SV_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent);
74 if (trace.fraction == 1.0)
76 mid = bottom = trace.endpos[2];
78 // the corners must be within 16 of the midpoint
79 for (x=0 ; x<=1 ; x++)
80 for (y=0 ; y<=1 ; y++)
82 start[0] = stop[0] = x ? maxs[0] : mins[0];
83 start[1] = stop[1] = y ? maxs[1] : mins[1];
85 trace = SV_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, ent);
87 if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
88 bottom = trace.endpos[2];
89 if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value)
102 Called by monster program code.
103 The move will be adjusted for slopes and stairs, but if the move isn't
104 possible, no move is done, false is returned, and
105 prog->globals.server->trace_normal is set to the normal of the blocking wall
108 qboolean SV_movestep (prvm_edict_t *ent, vec3_t move, qboolean relink)
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);
127 enemy = PRVM_PROG_TO_EDICT(ent->fields.server->enemy);
128 if (i == 0 && enemy != prog->edicts)
130 dz = ent->fields.server->origin[2] - PRVM_PROG_TO_EDICT(ent->fields.server->enemy)->fields.server->origin[2];
136 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, neworg, MOVE_NORMAL, ent);
138 if (trace.fraction == 1)
140 VectorCopy(trace.endpos, traceendpos);
141 if (((int)ent->fields.server->flags & FL_SWIM) && !(SV_PointSuperContents(traceendpos) & SUPERCONTENTS_LIQUIDSMASK))
142 return false; // swim monster left water
144 VectorCopy (traceendpos, ent->fields.server->origin);
146 SV_LinkEdict (ent, true);
150 if (enemy == prog->edicts)
157 // push down from a step height above the wished position
158 neworg[2] += sv_stepheight.value;
159 VectorCopy (neworg, end);
160 end[2] -= sv_stepheight.value*2;
162 trace = SV_Move (neworg, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
164 if (trace.startsolid)
166 neworg[2] -= sv_stepheight.value;
167 trace = SV_Move (neworg, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
168 if (trace.startsolid)
171 if (trace.fraction == 1)
173 // if monster had the ground pulled out, go ahead and fall
174 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
176 VectorAdd (ent->fields.server->origin, move, ent->fields.server->origin);
178 SV_LinkEdict (ent, true);
179 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
183 return false; // walked off an edge
186 // check point traces down for dangling corners
187 VectorCopy (trace.endpos, ent->fields.server->origin);
189 if (!SV_CheckBottom (ent))
191 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
192 { // entity had floor mostly pulled out from underneath it
193 // and is trying to correct
195 SV_LinkEdict (ent, true);
198 VectorCopy (oldorg, ent->fields.server->origin);
202 if ( (int)ent->fields.server->flags & FL_PARTIALGROUND )
203 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_PARTIALGROUND;
205 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
209 SV_LinkEdict (ent, true);
214 //============================================================================
217 ======================
220 Turns to the movement direction, and walks the current distance if
223 ======================
225 void VM_changeyaw (void);
226 qboolean SV_StepDirection (prvm_edict_t *ent, float yaw, float dist)
228 vec3_t move, oldorigin;
231 ent->fields.server->ideal_yaw = yaw;
234 yaw = yaw*M_PI*2 / 360;
235 move[0] = cos(yaw)*dist;
236 move[1] = sin(yaw)*dist;
239 VectorCopy (ent->fields.server->origin, oldorigin);
240 if (SV_movestep (ent, move, false))
242 delta = ent->fields.server->angles[YAW] - ent->fields.server->ideal_yaw;
243 if (delta > 45 && delta < 315)
244 { // not turned far enough, so don't take the step
245 VectorCopy (oldorigin, ent->fields.server->origin);
247 SV_LinkEdict (ent, true);
250 SV_LinkEdict (ent, true);
256 ======================
259 ======================
261 void SV_FixCheckBottom (prvm_edict_t *ent)
263 ent->fields.server->flags = (int)ent->fields.server->flags | FL_PARTIALGROUND;
275 void SV_NewChaseDir (prvm_edict_t *actor, prvm_edict_t *enemy, float dist)
279 float tdir, olddir, turnaround;
281 olddir = ANGLEMOD((int)(actor->fields.server->ideal_yaw/45)*45);
282 turnaround = ANGLEMOD(olddir - 180);
284 deltax = enemy->fields.server->origin[0] - actor->fields.server->origin[0];
285 deltay = enemy->fields.server->origin[1] - actor->fields.server->origin[1];
300 if (d[1] != DI_NODIR && d[2] != DI_NODIR)
303 tdir = d[2] == 90 ? 45 : 315;
305 tdir = d[2] == 90 ? 135 : 215;
307 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
311 // try other directions
312 if ( ((rand()&3) & 1) || fabs(deltay)>fabs(deltax))
319 if (d[1]!=DI_NODIR && d[1]!=turnaround
320 && SV_StepDirection(actor, d[1], dist))
323 if (d[2]!=DI_NODIR && d[2]!=turnaround
324 && SV_StepDirection(actor, d[2], dist))
327 /* there is no direct path to the player, so pick another direction */
329 if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
332 if (rand()&1) /*randomly determine direction of search*/
334 for (tdir=0 ; tdir<=315 ; tdir += 45)
335 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
340 for (tdir=315 ; tdir >=0 ; tdir -= 45)
341 if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
345 if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
348 actor->fields.server->ideal_yaw = olddir; // can't move
350 // if a bridge was pulled out from underneath a monster, it may not have
351 // a valid standing position at all
353 if (!SV_CheckBottom (actor))
354 SV_FixCheckBottom (actor);
359 ======================
362 ======================
364 qboolean SV_CloseEnough (prvm_edict_t *ent, prvm_edict_t *goal, float dist)
368 for (i=0 ; i<3 ; i++)
370 if (goal->priv.server->areamins[i] > ent->priv.server->areamaxs[i] + dist)
372 if (goal->priv.server->areamaxs[i] < ent->priv.server->areamins[i] - dist)
379 ======================
382 ======================
384 void SV_MoveToGoal (void)
386 prvm_edict_t *ent, *goal;
389 ent = PRVM_PROG_TO_EDICT(prog->globals.server->self);
390 goal = PRVM_PROG_TO_EDICT(ent->fields.server->goalentity);
391 dist = PRVM_G_FLOAT(OFS_PARM0);
393 if ( !( (int)ent->fields.server->flags & (FL_ONGROUND|FL_FLY|FL_SWIM) ) )
395 PRVM_G_FLOAT(OFS_RETURN) = 0;
399 // if the next step hits the enemy, return immediately
400 if ( PRVM_PROG_TO_EDICT(ent->fields.server->enemy) != prog->edicts && SV_CloseEnough (ent, goal, dist) )
404 if ( (rand()&3)==1 ||
405 !SV_StepDirection (ent, ent->fields.server->ideal_yaw, dist))
407 SV_NewChaseDir (ent, goal, dist);