]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_phys.c
significantly reworked the cl_movement code, now predicts quakeworld almost completel...
[divverent/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23 // used only for VM_GetTempString
24 #include "prvm_cmds.h"
25
26 /*
27
28
29 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
30
31 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
32
33 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
34 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
35 corpses are SOLID_NOT and MOVETYPE_TOSS
36 crates are SOLID_BBOX and MOVETYPE_TOSS
37 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
38 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39
40 solid_edge items only clip against bsp models.
41
42 */
43
44 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
45 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
46 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
47 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
48 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
49 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
50 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
51 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
52 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
53 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
54 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
55
56 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
57 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
58
59 #define MOVE_EPSILON    0.01
60
61 void SV_Physics_Toss (prvm_edict_t *ent);
62
63 void SV_Phys_Init (void)
64 {
65         Cvar_RegisterVariable(&sv_stepheight);
66         Cvar_RegisterVariable(&sv_jumpstep);
67         Cvar_RegisterVariable(&sv_wallfriction);
68         Cvar_RegisterVariable(&sv_newflymove);
69         Cvar_RegisterVariable(&sv_freezenonclients);
70
71         Cvar_RegisterVariable(&sv_playerphysicsqc);
72
73         Cvar_RegisterVariable(&sv_sound_watersplash);
74         Cvar_RegisterVariable(&sv_sound_land);
75 }
76
77 /*
78 ============
79 SV_TestEntityPosition
80
81 returns true if the entity is in solid currently
82 ============
83 */
84 static int SV_TestEntityPosition (prvm_edict_t *ent)
85 {
86         trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
87         if (trace.startsupercontents & SUPERCONTENTS_SOLID)
88                 return true;
89         else
90                 return false;
91 }
92
93 /*
94 ================
95 SV_CheckAllEnts
96 ================
97 */
98 void SV_CheckAllEnts (void)
99 {
100         int e;
101         prvm_edict_t *check;
102
103         // see if any solid entities are inside the final position
104         check = PRVM_NEXT_EDICT(prog->edicts);
105         for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
106         {
107                 if (check->priv.server->free)
108                         continue;
109                 if (check->fields.server->movetype == MOVETYPE_PUSH
110                  || check->fields.server->movetype == MOVETYPE_NONE
111                  || check->fields.server->movetype == MOVETYPE_FOLLOW
112                  || check->fields.server->movetype == MOVETYPE_NOCLIP)
113                         continue;
114
115                 if (SV_TestEntityPosition (check))
116                         Con_Print("entity in invalid position\n");
117         }
118 }
119
120 /*
121 ================
122 SV_CheckVelocity
123 ================
124 */
125 void SV_CheckVelocity (prvm_edict_t *ent)
126 {
127         int i;
128         float wishspeed;
129
130 //
131 // bound velocity
132 //
133         for (i=0 ; i<3 ; i++)
134         {
135                 if (IS_NAN(ent->fields.server->velocity[i]))
136                 {
137                         Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
138                         ent->fields.server->velocity[i] = 0;
139                 }
140                 if (IS_NAN(ent->fields.server->origin[i]))
141                 {
142                         Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
143                         ent->fields.server->origin[i] = 0;
144                 }
145         }
146
147         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
148         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
149         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
150         {
151                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
152                 ent->fields.server->velocity[0] *= wishspeed;
153                 ent->fields.server->velocity[1] *= wishspeed;
154                 ent->fields.server->velocity[2] *= wishspeed;
155         }
156 }
157
158 /*
159 =============
160 SV_RunThink
161
162 Runs thinking code if time.  There is some play in the exact time the think
163 function will be called, because it is called before any movement is done
164 in a frame.  Not used for pushmove objects, because they must be exact.
165 Returns false if the entity removed itself.
166 =============
167 */
168 qboolean SV_RunThink (prvm_edict_t *ent)
169 {
170         float thinktime;
171
172         thinktime = ent->fields.server->nextthink;
173         if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
174                 return true;
175
176         // don't let things stay in the past.
177         // it is possible to start that way by a trigger with a local time.
178         if (thinktime < sv.time)
179                 thinktime = sv.time;
180
181         ent->fields.server->nextthink = 0;
182         prog->globals.server->time = thinktime;
183         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
184         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
185         PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
186         return !ent->priv.server->free;
187 }
188
189 /*
190 ==================
191 SV_Impact
192
193 Two entities have touched, so run their touch functions
194 ==================
195 */
196 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
197 {
198         int old_self, old_other;
199         prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
200         prvm_eval_t *val;
201
202         old_self = prog->globals.server->self;
203         old_other = prog->globals.server->other;
204
205         prog->globals.server->time = sv.time;
206         if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
207         {
208                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
209                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
210                 prog->globals.server->trace_allsolid = trace->allsolid;
211                 prog->globals.server->trace_startsolid = trace->startsolid;
212                 prog->globals.server->trace_fraction = trace->fraction;
213                 prog->globals.server->trace_inwater = trace->inwater;
214                 prog->globals.server->trace_inopen = trace->inopen;
215                 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
216                 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
217                 prog->globals.server->trace_plane_dist =  trace->plane.dist;
218                 if (trace->ent)
219                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
220                 else
221                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
222                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
223                         val->_float = trace->startsupercontents;
224                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
225                         val->_float = trace->hitsupercontents;
226                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
227                         val->_float = trace->hitq3surfaceflags;
228                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
229                 {
230                         if (trace->hittexture)
231                         {
232                                 char *s = VM_GetTempString();
233                                 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
234                                 val->string = PRVM_SetEngineString(s);
235                         }
236                         else
237                                 val->string = 0;
238                 }
239                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
240         }
241
242         if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
243         {
244                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
245                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
246                 prog->globals.server->trace_allsolid = false;
247                 prog->globals.server->trace_startsolid = false;
248                 prog->globals.server->trace_fraction = 1;
249                 prog->globals.server->trace_inwater = false;
250                 prog->globals.server->trace_inopen = true;
251                 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
252                 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
253                 prog->globals.server->trace_plane_dist = 0;
254                 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
255                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
256                         val->_float = 0;
257                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
258                         val->_float = 0;
259                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
260                         val->_float = 0;
261                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
262                         val->string = 0;
263                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
264         }
265
266         prog->globals.server->self = old_self;
267         prog->globals.server->other = old_other;
268 }
269
270
271 /*
272 ==================
273 ClipVelocity
274
275 Slide off of the impacting object
276 returns the blocked flags (1 = floor, 2 = step / wall)
277 ==================
278 */
279 #define STOP_EPSILON 0.1
280 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
281 {
282         int i;
283         float backoff;
284
285         backoff = -DotProduct (in, normal) * overbounce;
286         VectorMA(in, backoff, normal, out);
287
288         for (i = 0;i < 3;i++)
289                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
290                         out[i] = 0;
291 }
292
293
294 /*
295 ============
296 SV_FlyMove
297
298 The basic solid body movement clip that slides along multiple planes
299 Returns the clipflags if the velocity was modified (hit something solid)
300 1 = floor
301 2 = wall / step
302 4 = dead stop
303 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
304 ============
305 */
306 // LordHavoc: increased from 5 to 32
307 #define MAX_CLIP_PLANES 32
308 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
309 {
310         int blocked, bumpcount;
311         int i, j, impact, numplanes;
312         float d, time_left;
313         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
314         trace_t trace;
315         blocked = 0;
316         VectorCopy(ent->fields.server->velocity, original_velocity);
317         VectorCopy(ent->fields.server->velocity, primal_velocity);
318         numplanes = 0;
319         time_left = time;
320         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
321         {
322                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
323                         break;
324
325                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
326                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
327 #if 0
328                 //if (trace.fraction < 0.002)
329                 {
330 #if 1
331                         vec3_t start;
332                         trace_t testtrace;
333                         VectorCopy(ent->fields.server->origin, start);
334                         start[2] += 3;//0.03125;
335                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
336                         end[2] += 3;//0.03125;
337                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
338                         if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
339                         {
340                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
341                                 trace = testtrace;
342                         }
343 #endif
344 #if 0
345                         //j = -1;
346                         for (i = 0;i < numplanes;i++)
347                         {
348                                 VectorCopy(ent->fields.server->origin, start);
349                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
350                                 VectorMA(start, 3, planes[i], start);
351                                 VectorMA(end, 3, planes[i], end);
352                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
353                                 if (trace.fraction < testtrace.fraction)
354                                 {
355                                         trace = testtrace;
356                                         VectorCopy(start, ent->fields.server->origin);
357                                         //j = i;
358                                 }
359                         }
360                         //if (j >= 0)
361                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
362 #endif
363                 }
364 #endif
365
366 #if 0
367                 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
368                 if (trace.fraction < 1)
369                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
370                 Con_Print("\n");
371 #endif
372
373                 if (trace.bmodelstartsolid)
374                 {
375                         // LordHavoc: note: this code is what makes entities stick in place
376                         // if embedded in world only (you can walk through other objects if
377                         // stuck)
378                         // entity is trapped in another solid
379                         VectorClear(ent->fields.server->velocity);
380                         return 3;
381                 }
382
383                 // break if it moved the entire distance
384                 if (trace.fraction == 1)
385                 {
386                         VectorCopy(trace.endpos, ent->fields.server->origin);
387                         break;
388                 }
389
390                 if (!trace.ent)
391                 {
392                         Con_Printf ("SV_FlyMove: !trace.ent");
393                         trace.ent = prog->edicts;
394                 }
395
396                 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
397                         impact = false;
398                 else
399                 {
400                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
401                         impact = true;
402                 }
403
404                 if (trace.plane.normal[2])
405                 {
406                         if (trace.plane.normal[2] > 0.7)
407                         {
408                                 // floor
409                                 blocked |= 1;
410                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
411                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
412                         }
413                 }
414                 else
415                 {
416                         // step
417                         blocked |= 2;
418                         // save the trace for player extrafriction
419                         if (stepnormal)
420                                 VectorCopy(trace.plane.normal, stepnormal);
421                 }
422
423                 if (trace.fraction >= 0.001)
424                 {
425                         // actually covered some distance
426                         VectorCopy(trace.endpos, ent->fields.server->origin);
427                         VectorCopy(ent->fields.server->velocity, original_velocity);
428                         numplanes = 0;
429                 }
430
431                 // run the impact function
432                 if (impact)
433                 {
434                         SV_Impact(ent, &trace);
435
436                         // break if removed by the impact function
437                         if (ent->priv.server->free)
438                                 break;
439                 }
440
441                 time_left *= 1 - trace.fraction;
442
443                 // clipped to another plane
444                 if (numplanes >= MAX_CLIP_PLANES)
445                 {
446                         // this shouldn't really happen
447                         VectorClear(ent->fields.server->velocity);
448                         blocked = 3;
449                         break;
450                 }
451
452                 /*
453                 for (i = 0;i < numplanes;i++)
454                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
455                                 break;
456                 if (i < numplanes)
457                 {
458                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
459                         continue;
460                 }
461                 */
462
463                 VectorCopy(trace.plane.normal, planes[numplanes]);
464                 numplanes++;
465
466                 if (sv_newflymove.integer)
467                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
468                 else
469                 {
470                         // modify original_velocity so it parallels all of the clip planes
471                         for (i = 0;i < numplanes;i++)
472                         {
473                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
474                                 for (j = 0;j < numplanes;j++)
475                                 {
476                                         if (j != i)
477                                         {
478                                                 // not ok
479                                                 if (DotProduct(new_velocity, planes[j]) < 0)
480                                                         break;
481                                         }
482                                 }
483                                 if (j == numplanes)
484                                         break;
485                         }
486
487                         if (i != numplanes)
488                         {
489                                 // go along this plane
490                                 VectorCopy(new_velocity, ent->fields.server->velocity);
491                         }
492                         else
493                         {
494                                 // go along the crease
495                                 if (numplanes != 2)
496                                 {
497                                         VectorClear(ent->fields.server->velocity);
498                                         blocked = 7;
499                                         break;
500                                 }
501                                 CrossProduct(planes[0], planes[1], dir);
502                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
503                                 VectorNormalize(dir);
504                                 d = DotProduct(dir, ent->fields.server->velocity);
505                                 VectorScale(dir, d, ent->fields.server->velocity);
506                         }
507                 }
508
509                 // if current velocity is against the original velocity,
510                 // stop dead to avoid tiny occilations in sloping corners
511                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
512                 {
513                         VectorClear(ent->fields.server->velocity);
514                         break;
515                 }
516         }
517
518         //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
519
520         /*
521         if ((blocked & 1) == 0 && bumpcount > 1)
522         {
523                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
524                 // flag ONGROUND if there's ground under it
525                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
526         }
527         */
528         return blocked;
529 }
530
531 /*
532 ============
533 SV_AddGravity
534
535 ============
536 */
537 void SV_AddGravity (prvm_edict_t *ent)
538 {
539         float ent_gravity;
540         prvm_eval_t *val;
541
542         val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
543         if (val!=0 && val->_float)
544                 ent_gravity = val->_float;
545         else
546                 ent_gravity = 1.0;
547         ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
548 }
549
550
551 /*
552 ===============================================================================
553
554 PUSHMOVE
555
556 ===============================================================================
557 */
558
559 /*
560 ============
561 SV_PushEntity
562
563 Does not change the entities velocity at all
564 ============
565 */
566 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
567 {
568         int type;
569         trace_t trace;
570         vec3_t end;
571
572         VectorAdd (ent->fields.server->origin, push, end);
573
574         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
575                 type = MOVE_MISSILE;
576         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
577                 type = MOVE_NOMONSTERS; // only clip against bmodels
578         else
579                 type = MOVE_NORMAL;
580
581         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
582
583         VectorCopy (trace.endpos, ent->fields.server->origin);
584         SV_LinkEdict (ent, true);
585
586         if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
587                 SV_Impact (ent, &trace);
588         return trace;
589 }
590
591
592 /*
593 ============
594 SV_PushMove
595
596 ============
597 */
598 void SV_PushMove (prvm_edict_t *pusher, float movetime)
599 {
600         int i, e, index;
601         float savesolid, movetime2, pushltime;
602         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
603         int num_moved;
604         int numcheckentities;
605         static prvm_edict_t *checkentities[MAX_EDICTS];
606         model_t *pushermodel;
607         trace_t trace;
608
609         if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
610         {
611                 pusher->fields.server->ltime += movetime;
612                 return;
613         }
614
615         switch ((int) pusher->fields.server->solid)
616         {
617         // LordHavoc: valid pusher types
618         case SOLID_BSP:
619         case SOLID_BBOX:
620         case SOLID_SLIDEBOX:
621         case SOLID_CORPSE: // LordHavoc: this would be weird...
622                 break;
623         // LordHavoc: no collisions
624         case SOLID_NOT:
625         case SOLID_TRIGGER:
626                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
627                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
628                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
629                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
630                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
631                 pusher->fields.server->ltime += movetime;
632                 SV_LinkEdict (pusher, false);
633                 return;
634         default:
635                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
636                 return;
637         }
638         index = (int) pusher->fields.server->modelindex;
639         if (index < 1 || index >= MAX_MODELS)
640         {
641                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
642                 return;
643         }
644         pushermodel = sv.models[index];
645
646         movetime2 = movetime;
647         VectorScale(pusher->fields.server->velocity, movetime2, move1);
648         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
649         if (moveangle[0] || moveangle[2])
650         {
651                 for (i = 0;i < 3;i++)
652                 {
653                         if (move1[i] > 0)
654                         {
655                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
656                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
657                         }
658                         else
659                         {
660                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
661                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
662                         }
663                 }
664         }
665         else if (moveangle[1])
666         {
667                 for (i = 0;i < 3;i++)
668                 {
669                         if (move1[i] > 0)
670                         {
671                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
672                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
673                         }
674                         else
675                         {
676                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
677                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
678                         }
679                 }
680         }
681         else
682         {
683                 for (i = 0;i < 3;i++)
684                 {
685                         if (move1[i] > 0)
686                         {
687                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
688                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
689                         }
690                         else
691                         {
692                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
693                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
694                         }
695                 }
696         }
697
698         VectorNegate (moveangle, a);
699         AngleVectorsFLU (a, forward, left, up);
700
701         VectorCopy (pusher->fields.server->origin, pushorig);
702         VectorCopy (pusher->fields.server->angles, pushang);
703         pushltime = pusher->fields.server->ltime;
704
705 // move the pusher to its final position
706
707         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
708         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
709         pusher->fields.server->ltime += movetime;
710         SV_LinkEdict (pusher, false);
711
712         savesolid = pusher->fields.server->solid;
713
714 // see if any solid entities are inside the final position
715         num_moved = 0;
716
717         numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
718         for (e = 0;e < numcheckentities;e++)
719         {
720                 prvm_edict_t *check = checkentities[e];
721                 if (check->fields.server->movetype == MOVETYPE_NONE
722                  || check->fields.server->movetype == MOVETYPE_PUSH
723                  || check->fields.server->movetype == MOVETYPE_FOLLOW
724                  || check->fields.server->movetype == MOVETYPE_NOCLIP
725                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
726                         continue;
727
728                 // if the entity is standing on the pusher, it will definitely be moved
729                 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
730                 {
731                         // if the entity is not inside the pusher's final position, leave it alone
732                         if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
733                                 continue;
734                         // remove the onground flag for non-players
735                         if (check->fields.server->movetype != MOVETYPE_WALK)
736                                 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
737                 }
738
739
740                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
741                 {
742                         vec3_t org2;
743                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
744                         org2[0] = DotProduct (org, forward);
745                         org2[1] = DotProduct (org, left);
746                         org2[2] = DotProduct (org, up);
747                         VectorSubtract (org2, org, move);
748                         VectorAdd (move, move1, move);
749                 }
750                 else
751                         VectorCopy (move1, move);
752
753                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
754                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
755                 sv.moved_edicts[num_moved++] = check;
756
757                 // try moving the contacted entity
758                 pusher->fields.server->solid = SOLID_NOT;
759                 trace = SV_PushEntity (check, move);
760                 // FIXME: turn players specially
761                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
762                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
763
764                 // if it is still inside the pusher, block
765                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
766                 {
767                         // try moving the contacted entity a tiny bit further to account for precision errors
768                         vec3_t move2;
769                         pusher->fields.server->solid = SOLID_NOT;
770                         VectorScale(move, 1.1, move2);
771                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
772                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
773                         SV_PushEntity (check, move2);
774                         pusher->fields.server->solid = savesolid;
775                         if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
776                         {
777                                 // try moving the contacted entity a tiny bit less to account for precision errors
778                                 pusher->fields.server->solid = SOLID_NOT;
779                                 VectorScale(move, 0.9, move2);
780                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
781                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
782                                 SV_PushEntity (check, move2);
783                                 pusher->fields.server->solid = savesolid;
784                                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
785                                 {
786                                         // still inside pusher, so it's really blocked
787
788                                         // fail the move
789                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
790                                                 continue;
791                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
792                                         {
793                                                 // corpse
794                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
795                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
796                                                 continue;
797                                         }
798
799                                         VectorCopy (pushorig, pusher->fields.server->origin);
800                                         VectorCopy (pushang, pusher->fields.server->angles);
801                                         pusher->fields.server->ltime = pushltime;
802                                         SV_LinkEdict (pusher, false);
803
804                                         // move back any entities we already moved
805                                         for (i = 0;i < num_moved;i++)
806                                         {
807                                                 prvm_edict_t *ed = sv.moved_edicts[i];
808                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
809                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
810                                                 SV_LinkEdict (ed, false);
811                                         }
812
813                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
814                                         if (pusher->fields.server->blocked)
815                                         {
816                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
817                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
818                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
819                                         }
820                                         break;
821                                 }
822                         }
823                 }
824         }
825         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
826         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
827         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
828 }
829
830 /*
831 ================
832 SV_Physics_Pusher
833
834 ================
835 */
836 void SV_Physics_Pusher (prvm_edict_t *ent)
837 {
838         float thinktime, oldltime, movetime;
839
840         oldltime = ent->fields.server->ltime;
841
842         thinktime = ent->fields.server->nextthink;
843         if (thinktime < ent->fields.server->ltime + sv.frametime)
844         {
845                 movetime = thinktime - ent->fields.server->ltime;
846                 if (movetime < 0)
847                         movetime = 0;
848         }
849         else
850                 movetime = sv.frametime;
851
852         if (movetime)
853                 // advances ent->fields.server->ltime if not blocked
854                 SV_PushMove (ent, movetime);
855
856         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
857         {
858                 ent->fields.server->nextthink = 0;
859                 prog->globals.server->time = sv.time;
860                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
861                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
862                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
863         }
864 }
865
866
867 /*
868 ===============================================================================
869
870 CLIENT MOVEMENT
871
872 ===============================================================================
873 */
874
875 /*
876 =============
877 SV_CheckStuck
878
879 This is a big hack to try and fix the rare case of getting stuck in the world
880 clipping hull.
881 =============
882 */
883 void SV_CheckStuck (prvm_edict_t *ent)
884 {
885         int i, j, z;
886         vec3_t org;
887
888         if (!SV_TestEntityPosition(ent))
889         {
890                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
891                 return;
892         }
893
894         VectorCopy (ent->fields.server->origin, org);
895         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
896         if (!SV_TestEntityPosition(ent))
897         {
898                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
899                 SV_LinkEdict (ent, true);
900                 return;
901         }
902
903         for (z=-1 ; z< 18 ; z++)
904                 for (i=-1 ; i <= 1 ; i++)
905                         for (j=-1 ; j <= 1 ; j++)
906                         {
907                                 ent->fields.server->origin[0] = org[0] + i;
908                                 ent->fields.server->origin[1] = org[1] + j;
909                                 ent->fields.server->origin[2] = org[2] + z;
910                                 if (!SV_TestEntityPosition(ent))
911                                 {
912                                         Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
913                                         SV_LinkEdict (ent, true);
914                                         return;
915                                 }
916                         }
917
918         VectorCopy (org, ent->fields.server->origin);
919         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
920 }
921
922 static void SV_UnstickEntity (prvm_edict_t *ent)
923 {
924         int i, j, z;
925         vec3_t org;
926
927         // if not stuck in a bmodel, just return
928         if (!SV_TestEntityPosition(ent))
929                 return;
930
931         VectorCopy (ent->fields.server->origin, org);
932
933         for (z=-1 ; z< 18 ; z += 6)
934                 for (i=-1 ; i <= 1 ; i++)
935                         for (j=-1 ; j <= 1 ; j++)
936                         {
937                                 ent->fields.server->origin[0] = org[0] + i;
938                                 ent->fields.server->origin[1] = org[1] + j;
939                                 ent->fields.server->origin[2] = org[2] + z;
940                                 if (!SV_TestEntityPosition(ent))
941                                 {
942                                         Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
943                                         SV_LinkEdict (ent, true);
944                                         return;
945                                 }
946                         }
947
948         VectorCopy (org, ent->fields.server->origin);
949         Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
950 }
951
952
953 /*
954 =============
955 SV_CheckWater
956 =============
957 */
958 qboolean SV_CheckWater (prvm_edict_t *ent)
959 {
960         int cont;
961         vec3_t point;
962
963         point[0] = ent->fields.server->origin[0];
964         point[1] = ent->fields.server->origin[1];
965         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
966
967         ent->fields.server->waterlevel = 0;
968         ent->fields.server->watertype = CONTENTS_EMPTY;
969         cont = SV_PointSuperContents(point);
970         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
971         {
972                 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
973                 ent->fields.server->waterlevel = 1;
974                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
975                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
976                 {
977                         ent->fields.server->waterlevel = 2;
978                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
979                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
980                                 ent->fields.server->waterlevel = 3;
981                 }
982         }
983
984         return ent->fields.server->waterlevel > 1;
985 }
986
987 /*
988 ============
989 SV_WallFriction
990
991 ============
992 */
993 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
994 {
995         float d, i;
996         vec3_t forward, into, side;
997
998         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
999         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1000         {
1001                 // cut the tangential velocity
1002                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1003                 VectorScale (stepnormal, i, into);
1004                 VectorSubtract (ent->fields.server->velocity, into, side);
1005                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1006                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1007         }
1008 }
1009
1010 /*
1011 =====================
1012 SV_TryUnstick
1013
1014 Player has come to a dead stop, possibly due to the problem with limited
1015 float precision at some angle joins in the BSP hull.
1016
1017 Try fixing by pushing one pixel in each direction.
1018
1019 This is a hack, but in the interest of good gameplay...
1020 ======================
1021 */
1022 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1023 {
1024         int i, clip;
1025         vec3_t oldorg, dir;
1026
1027         VectorCopy (ent->fields.server->origin, oldorg);
1028         VectorClear (dir);
1029
1030         for (i=0 ; i<8 ; i++)
1031         {
1032                 // try pushing a little in an axial direction
1033                 switch (i)
1034                 {
1035                         case 0: dir[0] = 2; dir[1] = 0; break;
1036                         case 1: dir[0] = 0; dir[1] = 2; break;
1037                         case 2: dir[0] = -2; dir[1] = 0; break;
1038                         case 3: dir[0] = 0; dir[1] = -2; break;
1039                         case 4: dir[0] = 2; dir[1] = 2; break;
1040                         case 5: dir[0] = -2; dir[1] = 2; break;
1041                         case 6: dir[0] = 2; dir[1] = -2; break;
1042                         case 7: dir[0] = -2; dir[1] = -2; break;
1043                 }
1044
1045                 SV_PushEntity (ent, dir);
1046
1047                 // retry the original move
1048                 ent->fields.server->velocity[0] = oldvel[0];
1049                 ent->fields.server->velocity[1] = oldvel[1];
1050                 ent->fields.server->velocity[2] = 0;
1051                 clip = SV_FlyMove (ent, 0.1, NULL);
1052
1053                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1054                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1055                 {
1056                         Con_DPrint("TryUnstick - success.\n");
1057                         return clip;
1058                 }
1059
1060                 // go back to the original pos and try again
1061                 VectorCopy (oldorg, ent->fields.server->origin);
1062         }
1063
1064         // still not moving
1065         VectorClear (ent->fields.server->velocity);
1066         Con_DPrint("TryUnstick - failure.\n");
1067         return 7;
1068 }
1069
1070 /*
1071 =====================
1072 SV_WalkMove
1073
1074 Only used by players
1075 ======================
1076 */
1077 void SV_WalkMove (prvm_edict_t *ent)
1078 {
1079         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1080         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1081         trace_t downtrace;
1082
1083         SV_CheckVelocity(ent);
1084
1085         // do a regular slide move unless it looks like you ran into a step
1086         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1087         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1088
1089         VectorCopy (ent->fields.server->origin, start_origin);
1090         VectorCopy (ent->fields.server->velocity, start_velocity);
1091
1092         clip = SV_FlyMove (ent, sv.frametime, NULL);
1093
1094         SV_CheckVelocity(ent);
1095
1096         VectorCopy(ent->fields.server->origin, originalmove_origin);
1097         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1098         originalmove_clip = clip;
1099         originalmove_flags = (int)ent->fields.server->flags;
1100         originalmove_groundentity = ent->fields.server->groundentity;
1101
1102         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1103                 return;
1104
1105         if (sv_nostep.integer)
1106                 return;
1107
1108         // if move didn't block on a step, return
1109         if (clip & 2)
1110         {
1111                 // if move was not trying to move into the step, return
1112                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1113                         return;
1114
1115                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1116                 {
1117                         // return if gibbed by a trigger
1118                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1119                                 return;
1120
1121                         // only step up while jumping if that is enabled
1122                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1123                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1124                                         return;
1125                 }
1126
1127                 // try moving up and forward to go up a step
1128                 // back to start pos
1129                 VectorCopy (start_origin, ent->fields.server->origin);
1130                 VectorCopy (start_velocity, ent->fields.server->velocity);
1131
1132                 // move up
1133                 VectorClear (upmove);
1134                 upmove[2] = sv_stepheight.value;
1135                 // FIXME: don't link?
1136                 SV_PushEntity(ent, upmove);
1137
1138                 // move forward
1139                 ent->fields.server->velocity[2] = 0;
1140                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1141                 ent->fields.server->velocity[2] += start_velocity[2];
1142
1143                 SV_CheckVelocity(ent);
1144
1145                 // check for stuckness, possibly due to the limited precision of floats
1146                 // in the clipping hulls
1147                 if (clip
1148                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1149                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1150                 {
1151                         //Con_Printf("wall\n");
1152                         // stepping up didn't make any progress, revert to original move
1153                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1154                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1155                         //clip = originalmove_clip;
1156                         ent->fields.server->flags = originalmove_flags;
1157                         ent->fields.server->groundentity = originalmove_groundentity;
1158                         // now try to unstick if needed
1159                         //clip = SV_TryUnstick (ent, oldvel);
1160                         return;
1161                 }
1162
1163                 //Con_Printf("step - ");
1164
1165                 // extra friction based on view angle
1166                 if (clip & 2 && sv_wallfriction.integer)
1167                         SV_WallFriction (ent, stepnormal);
1168         }
1169         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1170         else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1171                 return;
1172
1173         // move down
1174         VectorClear (downmove);
1175         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1176         // FIXME: don't link?
1177         downtrace = SV_PushEntity (ent, downmove);
1178
1179         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1180         {
1181                 // this has been disabled so that you can't jump when you are stepping
1182                 // up while already jumping (also known as the Quake2 stair jump bug)
1183 #if 0
1184                 // LordHavoc: disabled this check so you can walk on monsters/players
1185                 //if (ent->fields.server->solid == SOLID_BSP)
1186                 {
1187                         //Con_Printf("onground\n");
1188                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1189                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1190                 }
1191 #endif
1192         }
1193         else
1194         {
1195                 //Con_Printf("slope\n");
1196                 // if the push down didn't end up on good ground, use the move without
1197                 // the step up.  This happens near wall / slope combinations, and can
1198                 // cause the player to hop up higher on a slope too steep to climb
1199                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1200                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1201                 //clip = originalmove_clip;
1202                 ent->fields.server->flags = originalmove_flags;
1203                 ent->fields.server->groundentity = originalmove_groundentity;
1204         }
1205
1206         SV_CheckVelocity(ent);
1207 }
1208
1209 //============================================================================
1210
1211 /*
1212 =============
1213 SV_Physics_Follow
1214
1215 Entities that are "stuck" to another entity
1216 =============
1217 */
1218 void SV_Physics_Follow (prvm_edict_t *ent)
1219 {
1220         vec3_t vf, vr, vu, angles, v;
1221         prvm_edict_t *e;
1222
1223         // regular thinking
1224         if (!SV_RunThink (ent))
1225                 return;
1226
1227         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1228         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1229         if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
1230         {
1231                 // quick case for no rotation
1232                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1233         }
1234         else
1235         {
1236                 angles[0] = -ent->fields.server->punchangle[0];
1237                 angles[1] =  ent->fields.server->punchangle[1];
1238                 angles[2] =  ent->fields.server->punchangle[2];
1239                 AngleVectors (angles, vf, vr, vu);
1240                 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
1241                 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
1242                 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
1243                 angles[0] = -e->fields.server->angles[0];
1244                 angles[1] =  e->fields.server->angles[1];
1245                 angles[2] =  e->fields.server->angles[2];
1246                 AngleVectors (angles, vf, vr, vu);
1247                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1248                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1249                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1250         }
1251         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1252         SV_LinkEdict (ent, true);
1253 }
1254
1255 /*
1256 ==============================================================================
1257
1258 TOSS / BOUNCE
1259
1260 ==============================================================================
1261 */
1262
1263 /*
1264 =============
1265 SV_CheckWaterTransition
1266
1267 =============
1268 */
1269 void SV_CheckWaterTransition (prvm_edict_t *ent)
1270 {
1271         int cont;
1272         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1273         if (!ent->fields.server->watertype)
1274         {
1275                 // just spawned here
1276                 ent->fields.server->watertype = cont;
1277                 ent->fields.server->waterlevel = 1;
1278                 return;
1279         }
1280
1281         // check if the entity crossed into or out of water
1282         if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1283                 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1284
1285         if (cont <= CONTENTS_WATER)
1286         {
1287                 ent->fields.server->watertype = cont;
1288                 ent->fields.server->waterlevel = 1;
1289         }
1290         else
1291         {
1292                 ent->fields.server->watertype = CONTENTS_EMPTY;
1293                 ent->fields.server->waterlevel = 0;
1294         }
1295 }
1296
1297 /*
1298 =============
1299 SV_Physics_Toss
1300
1301 Toss, bounce, and fly movement.  When onground, do nothing.
1302 =============
1303 */
1304 void SV_Physics_Toss (prvm_edict_t *ent)
1305 {
1306         trace_t trace;
1307         vec3_t move;
1308
1309 // if onground, return without moving
1310         if ((int)ent->fields.server->flags & FL_ONGROUND)
1311         {
1312                 // don't stick to ground if onground and moving upward
1313                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1314                         ent->fields.server->flags -= FL_ONGROUND;
1315                 else
1316                 {
1317                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1318                         if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1319                                 return;
1320                         // if ent was supported by a brush model on previous frame,
1321                         // and groundentity is now freed, set groundentity to 0 (floating)
1322                         if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1323                         {
1324                                 // leave it suspended in the air
1325                                 ent->fields.server->groundentity = 0;
1326                                 return;
1327                         }
1328                 }
1329         }
1330         ent->priv.server->suspendedinairflag = false;
1331
1332         SV_CheckVelocity (ent);
1333
1334 // add gravity
1335         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1336                 SV_AddGravity (ent);
1337
1338 // move angles
1339         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1340
1341 // move origin
1342         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1343         trace = SV_PushEntity (ent, move);
1344         if (ent->priv.server->free)
1345                 return;
1346         if (trace.bmodelstartsolid)
1347         {
1348                 // try to unstick the entity
1349                 SV_UnstickEntity(ent);
1350                 trace = SV_PushEntity (ent, move);
1351                 if (ent->priv.server->free)
1352                         return;
1353         }
1354
1355         if (trace.fraction < 1)
1356         {
1357                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1358                 {
1359                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1360                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1361                 }
1362                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1363                 {
1364                         float d;
1365                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1366                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1367                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1368                         {
1369                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1370                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1371                                 {
1372                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1373                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1374                                         VectorClear (ent->fields.server->velocity);
1375                                         VectorClear (ent->fields.server->avelocity);
1376                                 }
1377                                 else
1378                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1379                         }
1380                         else
1381                         {
1382                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1383                                 {
1384                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1385                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1386                                         VectorClear (ent->fields.server->velocity);
1387                                         VectorClear (ent->fields.server->avelocity);
1388                                 }
1389                                 else
1390                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1391                         }
1392                 }
1393                 else
1394                 {
1395                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1396                         if (trace.plane.normal[2] > 0.7)
1397                         {
1398                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1399                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1400                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1401                                         ent->priv.server->suspendedinairflag = true;
1402                                 VectorClear (ent->fields.server->velocity);
1403                                 VectorClear (ent->fields.server->avelocity);
1404                         }
1405                         else
1406                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1407                 }
1408         }
1409
1410 // check for in water
1411         SV_CheckWaterTransition (ent);
1412 }
1413
1414 /*
1415 ===============================================================================
1416
1417 STEPPING MOVEMENT
1418
1419 ===============================================================================
1420 */
1421
1422 /*
1423 =============
1424 SV_Physics_Step
1425
1426 Monsters freefall when they don't have a ground entity, otherwise
1427 all movement is done with discrete steps.
1428
1429 This is also used for objects that have become still on the ground, but
1430 will fall if the floor is pulled out from under them.
1431 =============
1432 */
1433 void SV_Physics_Step (prvm_edict_t *ent)
1434 {
1435         int flags = (int)ent->fields.server->flags;
1436         // don't fall at all if fly/swim
1437         if (!(flags & (FL_FLY | FL_SWIM)))
1438         {
1439                 if (flags & FL_ONGROUND)
1440                 {
1441                         // freefall if onground and moving upward
1442                         // freefall if not standing on a world surface (it may be a lift)
1443                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1444                         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1445                         {
1446                                 ent->fields.server->flags -= FL_ONGROUND;
1447                                 SV_AddGravity(ent);
1448                                 SV_CheckVelocity(ent);
1449                                 SV_FlyMove(ent, sv.frametime, NULL);
1450                                 SV_LinkEdict(ent, true);
1451                         }
1452                 }
1453                 else
1454                 {
1455                         // freefall if not onground
1456                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1457
1458                         SV_AddGravity(ent);
1459                         SV_CheckVelocity(ent);
1460                         SV_FlyMove(ent, sv.frametime, NULL);
1461                         SV_LinkEdict(ent, true);
1462
1463                         // just hit ground
1464                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1465                                 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1466                 }
1467         }
1468
1469 // regular thinking
1470         SV_RunThink(ent);
1471
1472         SV_CheckWaterTransition(ent);
1473 }
1474
1475 //============================================================================
1476
1477 static void SV_Physics_Entity (prvm_edict_t *ent)
1478 {
1479         // don't run a move on newly spawned projectiles as it messes up movement
1480         // interpolation and rocket trails
1481         qboolean runmove = ent->priv.server->move;
1482         ent->priv.server->move = true;
1483         switch ((int) ent->fields.server->movetype)
1484         {
1485         case MOVETYPE_PUSH:
1486         case MOVETYPE_FAKEPUSH:
1487                 SV_Physics_Pusher (ent);
1488                 break;
1489         case MOVETYPE_NONE:
1490                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1491                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1492                         SV_RunThink (ent);
1493                 break;
1494         case MOVETYPE_FOLLOW:
1495                 SV_Physics_Follow (ent);
1496                 break;
1497         case MOVETYPE_NOCLIP:
1498                 if (SV_RunThink(ent))
1499                 {
1500                         SV_CheckWater(ent);
1501                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1502                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1503                 }
1504                 SV_LinkEdict(ent, false);
1505                 break;
1506         case MOVETYPE_STEP:
1507                 SV_Physics_Step (ent);
1508                 break;
1509         case MOVETYPE_WALK:
1510                 if (SV_RunThink (ent))
1511                 {
1512                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1513                                 SV_AddGravity (ent);
1514                         SV_CheckStuck (ent);
1515                         SV_WalkMove (ent);
1516                         SV_LinkEdict (ent, true);
1517                 }
1518                 break;
1519         case MOVETYPE_TOSS:
1520         case MOVETYPE_BOUNCE:
1521         case MOVETYPE_BOUNCEMISSILE:
1522         case MOVETYPE_FLYMISSILE:
1523         case MOVETYPE_FLY:
1524                 // regular thinking
1525                 if (SV_RunThink (ent) && runmove)
1526                         SV_Physics_Toss (ent);
1527                 break;
1528         default:
1529                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1530                 break;
1531         }
1532 }
1533
1534 void SV_ApplyClientMove (void);
1535 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1536 {
1537         SV_ApplyClientMove();
1538         // make sure the velocity is sane (not a NaN)
1539         SV_CheckVelocity(ent);
1540         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1541         if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1542         {
1543                 prog->globals.server->time = sv.time;
1544                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1545                 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1546         }
1547         else
1548                 SV_ClientThink ();
1549         // make sure the velocity is sane (not a NaN)
1550         SV_CheckVelocity(ent);
1551         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1552         // player_run/player_stand1 does not horribly malfunction if the
1553         // velocity becomes a number that is both == 0 and != 0
1554         // (sounds to me like NaN but to be absolutely safe...)
1555         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1556                 VectorClear(ent->fields.server->velocity);
1557         // call standard client pre-think
1558         prog->globals.server->time = sv.time;
1559         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1560         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1561         SV_CheckVelocity (ent);
1562
1563         switch ((int) ent->fields.server->movetype)
1564         {
1565         case MOVETYPE_PUSH:
1566         case MOVETYPE_FAKEPUSH:
1567                 SV_Physics_Pusher (ent);
1568                 break;
1569         case MOVETYPE_NONE:
1570                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1571                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1572                         SV_RunThink (ent);
1573                 break;
1574         case MOVETYPE_FOLLOW:
1575                 SV_Physics_Follow (ent);
1576                 break;
1577         case MOVETYPE_NOCLIP:
1578                 if (SV_RunThink(ent))
1579                 {
1580                         SV_CheckWater(ent);
1581                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1582                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1583                 }
1584                 break;
1585         case MOVETYPE_STEP:
1586                 SV_Physics_Step (ent);
1587                 break;
1588         case MOVETYPE_WALK:
1589                 if (SV_RunThink (ent))
1590                 {
1591                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1592                                 SV_AddGravity (ent);
1593                         SV_CheckStuck (ent);
1594                         SV_WalkMove (ent);
1595                 }
1596                 break;
1597         case MOVETYPE_TOSS:
1598         case MOVETYPE_BOUNCE:
1599         case MOVETYPE_BOUNCEMISSILE:
1600         case MOVETYPE_FLYMISSILE:
1601                 // regular thinking
1602                 if (SV_RunThink (ent))
1603                         SV_Physics_Toss (ent);
1604                 break;
1605         case MOVETYPE_FLY:
1606                 if (SV_RunThink (ent))
1607                 {
1608                         SV_CheckWater (ent);
1609                         SV_WalkMove (ent);
1610                 }
1611                 break;
1612         default:
1613                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1614                 break;
1615         }
1616
1617         SV_CheckVelocity (ent);
1618
1619         // call standard player post-think
1620         SV_LinkEdict (ent, true);
1621
1622         SV_CheckVelocity (ent);
1623
1624         prog->globals.server->time = sv.time;
1625         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1626         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1627 }
1628
1629 /*
1630 ================
1631 SV_Physics
1632
1633 ================
1634 */
1635 void SV_Physics (void)
1636 {
1637         int i;
1638         prvm_edict_t *ent;
1639
1640 // let the progs know that a new frame has started
1641         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1642         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1643         prog->globals.server->time = sv.time;
1644         prog->globals.server->frametime = sv.frametime;
1645         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1646
1647 //
1648 // treat each object in turn
1649 //
1650
1651         // if force_retouch, relink all the entities
1652         if (prog->globals.server->force_retouch > 0)
1653                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1654                         if (!ent->priv.server->free)
1655                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
1656
1657         // run physics on the client entities
1658         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1659         {
1660                 if (!ent->priv.server->free)
1661                 {
1662                         // don't do physics on disconnected clients, FrikBot relies on this
1663                         if (!host_client->spawned)
1664                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1665                         // don't run physics here if running asynchronously
1666                         else if (!host_client->movesequence)
1667                                 SV_Physics_ClientEntity(ent);
1668                 }
1669         }
1670
1671         // run physics on all the non-client entities
1672         if (!sv_freezenonclients.integer)
1673                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1674                         if (!ent->priv.server->free)
1675                                 SV_Physics_Entity(ent);
1676
1677         if (prog->globals.server->force_retouch > 0)
1678                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1679
1680         // LordHavoc: endframe support
1681         if (EndFrameQC)
1682         {
1683                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1684                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1685                 prog->globals.server->time = sv.time;
1686                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1687         }
1688
1689         // decrement prog->num_edicts if the highest number entities died
1690         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1691
1692         if (!sv_freezenonclients.integer)
1693                 sv.time += sv.frametime;
1694 }
1695
1696
1697 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1698 {
1699         int i;
1700         float gravity;
1701         vec3_t move, end;
1702         vec3_t original_origin;
1703         vec3_t original_velocity;
1704         vec3_t original_angles;
1705         vec3_t original_avelocity;
1706         prvm_eval_t *val;
1707         trace_t trace;
1708
1709         VectorCopy(tossent->fields.server->origin   , original_origin   );
1710         VectorCopy(tossent->fields.server->velocity , original_velocity );
1711         VectorCopy(tossent->fields.server->angles   , original_angles   );
1712         VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1713
1714         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1715         if (val != NULL && val->_float != 0)
1716                 gravity = val->_float;
1717         else
1718                 gravity = 1.0;
1719         gravity *= sv_gravity.value * 0.05;
1720
1721         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1722         {
1723                 SV_CheckVelocity (tossent);
1724                 tossent->fields.server->velocity[2] -= gravity;
1725                 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1726                 VectorScale (tossent->fields.server->velocity, 0.05, move);
1727                 VectorAdd (tossent->fields.server->origin, move, end);
1728                 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1729                 VectorCopy (trace.endpos, tossent->fields.server->origin);
1730
1731                 if (trace.fraction < 1)
1732                         break;
1733         }
1734
1735         VectorCopy(original_origin   , tossent->fields.server->origin   );
1736         VectorCopy(original_velocity , tossent->fields.server->velocity );
1737         VectorCopy(original_angles   , tossent->fields.server->angles   );
1738         VectorCopy(original_avelocity, tossent->fields.server->avelocity);
1739
1740         return trace;
1741 }
1742