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