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