]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_phys.c
prevent Image_MipReduce from getting stuck in an infinite loop on 3D textures
[divverent/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23 // used only for VM_GetTempString
24 #include "prvm_cmds.h"
25
26 /*
27
28
29 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
30
31 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
32
33 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
34 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
35 corpses are SOLID_NOT and MOVETYPE_TOSS
36 crates are SOLID_BBOX and MOVETYPE_TOSS
37 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
38 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39
40 solid_edge items only clip against bsp models.
41
42 */
43
44 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
45 cvar_t sv_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                         // if the entity is not inside the pusher's final position, leave it alone
740                         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)
741                                 continue;
742                         // remove the onground flag for non-players
743                         if (check->fields.server->movetype != MOVETYPE_WALK)
744                                 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
745                 }
746
747
748                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
749                 {
750                         vec3_t org2;
751                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
752                         org2[0] = DotProduct (org, forward);
753                         org2[1] = DotProduct (org, left);
754                         org2[2] = DotProduct (org, up);
755                         VectorSubtract (org2, org, move);
756                         VectorAdd (move, move1, move);
757                 }
758                 else
759                         VectorCopy (move1, move);
760
761                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
762                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
763                 sv.moved_edicts[num_moved++] = check;
764
765                 // try moving the contacted entity
766                 pusher->fields.server->solid = SOLID_NOT;
767                 trace = SV_PushEntity (check, move);
768                 // FIXME: turn players specially
769                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
770                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
771
772                 // if it is still inside the pusher, block
773                 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)
774                 {
775                         // try moving the contacted entity a tiny bit further to account for precision errors
776                         vec3_t move2;
777                         pusher->fields.server->solid = SOLID_NOT;
778                         VectorScale(move, 1.1, move2);
779                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
780                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
781                         SV_PushEntity (check, move2);
782                         pusher->fields.server->solid = savesolid;
783                         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)
784                         {
785                                 // try moving the contacted entity a tiny bit less to account for precision errors
786                                 pusher->fields.server->solid = SOLID_NOT;
787                                 VectorScale(move, 0.9, move2);
788                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
789                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
790                                 SV_PushEntity (check, move2);
791                                 pusher->fields.server->solid = savesolid;
792                                 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)
793                                 {
794                                         // still inside pusher, so it's really blocked
795
796                                         // fail the move
797                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
798                                                 continue;
799                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
800                                         {
801                                                 // corpse
802                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
803                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
804                                                 continue;
805                                         }
806
807                                         VectorCopy (pushorig, pusher->fields.server->origin);
808                                         VectorCopy (pushang, pusher->fields.server->angles);
809                                         pusher->fields.server->ltime = pushltime;
810                                         SV_LinkEdict (pusher, false);
811
812                                         // move back any entities we already moved
813                                         for (i = 0;i < num_moved;i++)
814                                         {
815                                                 prvm_edict_t *ed = sv.moved_edicts[i];
816                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
817                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
818                                                 SV_LinkEdict (ed, false);
819                                         }
820
821                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
822                                         if (pusher->fields.server->blocked)
823                                         {
824                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
825                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
826                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
827                                         }
828                                         break;
829                                 }
830                         }
831                 }
832         }
833         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
834         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
835         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
836 }
837
838 /*
839 ================
840 SV_Physics_Pusher
841
842 ================
843 */
844 void SV_Physics_Pusher (prvm_edict_t *ent)
845 {
846         float thinktime, oldltime, movetime;
847
848         oldltime = ent->fields.server->ltime;
849
850         thinktime = ent->fields.server->nextthink;
851         if (thinktime < ent->fields.server->ltime + sv.frametime)
852         {
853                 movetime = thinktime - ent->fields.server->ltime;
854                 if (movetime < 0)
855                         movetime = 0;
856         }
857         else
858                 movetime = sv.frametime;
859
860         if (movetime)
861                 // advances ent->fields.server->ltime if not blocked
862                 SV_PushMove (ent, movetime);
863
864         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
865         {
866                 ent->fields.server->nextthink = 0;
867                 prog->globals.server->time = sv.time;
868                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
869                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
870                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
871         }
872 }
873
874
875 /*
876 ===============================================================================
877
878 CLIENT MOVEMENT
879
880 ===============================================================================
881 */
882
883 /*
884 =============
885 SV_CheckStuck
886
887 This is a big hack to try and fix the rare case of getting stuck in the world
888 clipping hull.
889 =============
890 */
891 void SV_CheckStuck (prvm_edict_t *ent)
892 {
893         int i, j, z;
894         vec3_t org;
895
896         if (!SV_TestEntityPosition(ent))
897         {
898                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
899                 return;
900         }
901
902         VectorCopy (ent->fields.server->origin, org);
903         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
904         if (!SV_TestEntityPosition(ent))
905         {
906                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
907                 SV_LinkEdict (ent, true);
908                 return;
909         }
910
911         for (z=-1 ; z< 18 ; z++)
912                 for (i=-1 ; i <= 1 ; i++)
913                         for (j=-1 ; j <= 1 ; j++)
914                         {
915                                 ent->fields.server->origin[0] = org[0] + i;
916                                 ent->fields.server->origin[1] = org[1] + j;
917                                 ent->fields.server->origin[2] = org[2] + z;
918                                 if (!SV_TestEntityPosition(ent))
919                                 {
920                                         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);
921                                         SV_LinkEdict (ent, true);
922                                         return;
923                                 }
924                         }
925
926         VectorCopy (org, ent->fields.server->origin);
927         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
928 }
929
930 static void SV_UnstickEntity (prvm_edict_t *ent)
931 {
932         int i, j, z;
933         vec3_t org;
934
935         // if not stuck in a bmodel, just return
936         if (!SV_TestEntityPosition(ent))
937                 return;
938
939         VectorCopy (ent->fields.server->origin, org);
940
941         for (z=-1 ; z< 18 ; z += 6)
942                 for (i=-1 ; i <= 1 ; i++)
943                         for (j=-1 ; j <= 1 ; j++)
944                         {
945                                 ent->fields.server->origin[0] = org[0] + i;
946                                 ent->fields.server->origin[1] = org[1] + j;
947                                 ent->fields.server->origin[2] = org[2] + z;
948                                 if (!SV_TestEntityPosition(ent))
949                                 {
950                                         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);
951                                         SV_LinkEdict (ent, true);
952                                         return;
953                                 }
954                         }
955
956         VectorCopy (org, ent->fields.server->origin);
957         Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
958 }
959
960
961 /*
962 =============
963 SV_CheckWater
964 =============
965 */
966 qboolean SV_CheckWater (prvm_edict_t *ent)
967 {
968         int cont;
969         vec3_t point;
970
971         point[0] = ent->fields.server->origin[0];
972         point[1] = ent->fields.server->origin[1];
973         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
974
975         ent->fields.server->waterlevel = 0;
976         ent->fields.server->watertype = CONTENTS_EMPTY;
977         cont = SV_PointSuperContents(point);
978         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
979         {
980                 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
981                 ent->fields.server->waterlevel = 1;
982                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
983                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
984                 {
985                         ent->fields.server->waterlevel = 2;
986                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
987                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
988                                 ent->fields.server->waterlevel = 3;
989                 }
990         }
991
992         return ent->fields.server->waterlevel > 1;
993 }
994
995 /*
996 ============
997 SV_WallFriction
998
999 ============
1000 */
1001 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1002 {
1003         float d, i;
1004         vec3_t forward, into, side;
1005
1006         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1007         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1008         {
1009                 // cut the tangential velocity
1010                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1011                 VectorScale (stepnormal, i, into);
1012                 VectorSubtract (ent->fields.server->velocity, into, side);
1013                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1014                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1015         }
1016 }
1017
1018 /*
1019 =====================
1020 SV_TryUnstick
1021
1022 Player has come to a dead stop, possibly due to the problem with limited
1023 float precision at some angle joins in the BSP hull.
1024
1025 Try fixing by pushing one pixel in each direction.
1026
1027 This is a hack, but in the interest of good gameplay...
1028 ======================
1029 */
1030 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1031 {
1032         int i, clip;
1033         vec3_t oldorg, dir;
1034
1035         VectorCopy (ent->fields.server->origin, oldorg);
1036         VectorClear (dir);
1037
1038         for (i=0 ; i<8 ; i++)
1039         {
1040                 // try pushing a little in an axial direction
1041                 switch (i)
1042                 {
1043                         case 0: dir[0] = 2; dir[1] = 0; break;
1044                         case 1: dir[0] = 0; dir[1] = 2; break;
1045                         case 2: dir[0] = -2; dir[1] = 0; break;
1046                         case 3: dir[0] = 0; dir[1] = -2; break;
1047                         case 4: dir[0] = 2; dir[1] = 2; break;
1048                         case 5: dir[0] = -2; dir[1] = 2; break;
1049                         case 6: dir[0] = 2; dir[1] = -2; break;
1050                         case 7: dir[0] = -2; dir[1] = -2; break;
1051                 }
1052
1053                 SV_PushEntity (ent, dir);
1054
1055                 // retry the original move
1056                 ent->fields.server->velocity[0] = oldvel[0];
1057                 ent->fields.server->velocity[1] = oldvel[1];
1058                 ent->fields.server->velocity[2] = 0;
1059                 clip = SV_FlyMove (ent, 0.1, NULL);
1060
1061                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1062                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1063                 {
1064                         Con_DPrint("TryUnstick - success.\n");
1065                         return clip;
1066                 }
1067
1068                 // go back to the original pos and try again
1069                 VectorCopy (oldorg, ent->fields.server->origin);
1070         }
1071
1072         // still not moving
1073         VectorClear (ent->fields.server->velocity);
1074         Con_DPrint("TryUnstick - failure.\n");
1075         return 7;
1076 }
1077
1078 /*
1079 =====================
1080 SV_WalkMove
1081
1082 Only used by players
1083 ======================
1084 */
1085 void SV_WalkMove (prvm_edict_t *ent)
1086 {
1087         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1088         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1089         trace_t downtrace;
1090
1091         SV_CheckVelocity(ent);
1092
1093         // do a regular slide move unless it looks like you ran into a step
1094         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1095         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1096
1097         VectorCopy (ent->fields.server->origin, start_origin);
1098         VectorCopy (ent->fields.server->velocity, start_velocity);
1099
1100         clip = SV_FlyMove (ent, sv.frametime, NULL);
1101
1102         SV_CheckVelocity(ent);
1103
1104         VectorCopy(ent->fields.server->origin, originalmove_origin);
1105         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1106         originalmove_clip = clip;
1107         originalmove_flags = (int)ent->fields.server->flags;
1108         originalmove_groundentity = ent->fields.server->groundentity;
1109
1110         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1111                 return;
1112
1113         if (sv_nostep.integer)
1114                 return;
1115
1116         // if move didn't block on a step, return
1117         if (clip & 2)
1118         {
1119                 // if move was not trying to move into the step, return
1120                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1121                         return;
1122
1123                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1124                 {
1125                         // return if gibbed by a trigger
1126                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1127                                 return;
1128
1129                         // only step up while jumping if that is enabled
1130                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1131                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1132                                         return;
1133                 }
1134
1135                 // try moving up and forward to go up a step
1136                 // back to start pos
1137                 VectorCopy (start_origin, ent->fields.server->origin);
1138                 VectorCopy (start_velocity, ent->fields.server->velocity);
1139
1140                 // move up
1141                 VectorClear (upmove);
1142                 upmove[2] = sv_stepheight.value;
1143                 // FIXME: don't link?
1144                 SV_PushEntity(ent, upmove);
1145
1146                 // move forward
1147                 ent->fields.server->velocity[2] = 0;
1148                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1149                 ent->fields.server->velocity[2] += start_velocity[2];
1150
1151                 SV_CheckVelocity(ent);
1152
1153                 // check for stuckness, possibly due to the limited precision of floats
1154                 // in the clipping hulls
1155                 if (clip
1156                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1157                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1158                 {
1159                         //Con_Printf("wall\n");
1160                         // stepping up didn't make any progress, revert to original move
1161                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1162                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1163                         //clip = originalmove_clip;
1164                         ent->fields.server->flags = originalmove_flags;
1165                         ent->fields.server->groundentity = originalmove_groundentity;
1166                         // now try to unstick if needed
1167                         //clip = SV_TryUnstick (ent, oldvel);
1168                         return;
1169                 }
1170
1171                 //Con_Printf("step - ");
1172
1173                 // extra friction based on view angle
1174                 if (clip & 2 && sv_wallfriction.integer)
1175                         SV_WallFriction (ent, stepnormal);
1176         }
1177         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1178         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)))
1179                 return;
1180
1181         // move down
1182         VectorClear (downmove);
1183         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1184         // FIXME: don't link?
1185         downtrace = SV_PushEntity (ent, downmove);
1186
1187         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1188         {
1189                 // this has been disabled so that you can't jump when you are stepping
1190                 // up while already jumping (also known as the Quake2 stair jump bug)
1191 #if 0
1192                 // LordHavoc: disabled this check so you can walk on monsters/players
1193                 //if (ent->fields.server->solid == SOLID_BSP)
1194                 {
1195                         //Con_Printf("onground\n");
1196                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1197                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1198                 }
1199 #endif
1200         }
1201         else
1202         {
1203                 //Con_Printf("slope\n");
1204                 // if the push down didn't end up on good ground, use the move without
1205                 // the step up.  This happens near wall / slope combinations, and can
1206                 // cause the player to hop up higher on a slope too steep to climb
1207                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1208                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1209                 //clip = originalmove_clip;
1210                 ent->fields.server->flags = originalmove_flags;
1211                 ent->fields.server->groundentity = originalmove_groundentity;
1212         }
1213
1214         SV_CheckVelocity(ent);
1215 }
1216
1217 //============================================================================
1218
1219 /*
1220 =============
1221 SV_Physics_Follow
1222
1223 Entities that are "stuck" to another entity
1224 =============
1225 */
1226 void SV_Physics_Follow (prvm_edict_t *ent)
1227 {
1228         vec3_t vf, vr, vu, angles, v;
1229         prvm_edict_t *e;
1230
1231         // regular thinking
1232         if (!SV_RunThink (ent))
1233                 return;
1234
1235         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1236         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1237         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])
1238         {
1239                 // quick case for no rotation
1240                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1241         }
1242         else
1243         {
1244                 angles[0] = -ent->fields.server->punchangle[0];
1245                 angles[1] =  ent->fields.server->punchangle[1];
1246                 angles[2] =  ent->fields.server->punchangle[2];
1247                 AngleVectors (angles, vf, vr, vu);
1248                 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];
1249                 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];
1250                 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];
1251                 angles[0] = -e->fields.server->angles[0];
1252                 angles[1] =  e->fields.server->angles[1];
1253                 angles[2] =  e->fields.server->angles[2];
1254                 AngleVectors (angles, vf, vr, vu);
1255                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1256                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1257                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1258         }
1259         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1260         SV_LinkEdict (ent, true);
1261 }
1262
1263 /*
1264 ==============================================================================
1265
1266 TOSS / BOUNCE
1267
1268 ==============================================================================
1269 */
1270
1271 /*
1272 =============
1273 SV_CheckWaterTransition
1274
1275 =============
1276 */
1277 void SV_CheckWaterTransition (prvm_edict_t *ent)
1278 {
1279         int cont;
1280         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1281         if (!ent->fields.server->watertype)
1282         {
1283                 // just spawned here
1284                 ent->fields.server->watertype = cont;
1285                 ent->fields.server->waterlevel = 1;
1286                 return;
1287         }
1288
1289         // check if the entity crossed into or out of water
1290         if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1291                 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1292
1293         if (cont <= CONTENTS_WATER)
1294         {
1295                 ent->fields.server->watertype = cont;
1296                 ent->fields.server->waterlevel = 1;
1297         }
1298         else
1299         {
1300                 ent->fields.server->watertype = CONTENTS_EMPTY;
1301                 ent->fields.server->waterlevel = 0;
1302         }
1303 }
1304
1305 /*
1306 =============
1307 SV_Physics_Toss
1308
1309 Toss, bounce, and fly movement.  When onground, do nothing.
1310 =============
1311 */
1312 void SV_Physics_Toss (prvm_edict_t *ent)
1313 {
1314         trace_t trace;
1315         vec3_t move;
1316
1317 // if onground, return without moving
1318         if ((int)ent->fields.server->flags & FL_ONGROUND)
1319         {
1320                 // don't stick to ground if onground and moving upward
1321                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1322                         ent->fields.server->flags -= FL_ONGROUND;
1323                 else
1324                 {
1325                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1326                         if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1327                                 return;
1328                         // if ent was supported by a brush model on previous frame,
1329                         // and groundentity is now freed, set groundentity to 0 (floating)
1330                         if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1331                         {
1332                                 // leave it suspended in the air
1333                                 ent->fields.server->groundentity = 0;
1334                                 return;
1335                         }
1336                 }
1337         }
1338         ent->priv.server->suspendedinairflag = false;
1339
1340         SV_CheckVelocity (ent);
1341
1342 // add gravity
1343         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1344                 SV_AddGravity (ent);
1345
1346 // move angles
1347         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1348
1349 // move origin
1350         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1351         trace = SV_PushEntity (ent, move);
1352         if (ent->priv.server->free)
1353                 return;
1354         if (trace.bmodelstartsolid)
1355         {
1356                 // try to unstick the entity
1357                 SV_UnstickEntity(ent);
1358                 trace = SV_PushEntity (ent, move);
1359                 if (ent->priv.server->free)
1360                         return;
1361         }
1362
1363         if (trace.fraction < 1)
1364         {
1365                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1366                 {
1367                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1368                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1369                 }
1370                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1371                 {
1372                         float d;
1373                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1374                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1375                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1376                         {
1377                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1378                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1379                                 {
1380                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1381                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1382                                         VectorClear (ent->fields.server->velocity);
1383                                         VectorClear (ent->fields.server->avelocity);
1384                                 }
1385                                 else
1386                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1387                         }
1388                         else
1389                         {
1390                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1391                                 {
1392                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1393                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1394                                         VectorClear (ent->fields.server->velocity);
1395                                         VectorClear (ent->fields.server->avelocity);
1396                                 }
1397                                 else
1398                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1399                         }
1400                 }
1401                 else
1402                 {
1403                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1404                         if (trace.plane.normal[2] > 0.7)
1405                         {
1406                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1407                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1408                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1409                                         ent->priv.server->suspendedinairflag = true;
1410                                 VectorClear (ent->fields.server->velocity);
1411                                 VectorClear (ent->fields.server->avelocity);
1412                         }
1413                         else
1414                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1415                 }
1416         }
1417
1418 // check for in water
1419         SV_CheckWaterTransition (ent);
1420 }
1421
1422 /*
1423 ===============================================================================
1424
1425 STEPPING MOVEMENT
1426
1427 ===============================================================================
1428 */
1429
1430 /*
1431 =============
1432 SV_Physics_Step
1433
1434 Monsters freefall when they don't have a ground entity, otherwise
1435 all movement is done with discrete steps.
1436
1437 This is also used for objects that have become still on the ground, but
1438 will fall if the floor is pulled out from under them.
1439 =============
1440 */
1441 void SV_Physics_Step (prvm_edict_t *ent)
1442 {
1443         int flags = (int)ent->fields.server->flags;
1444         // don't fall at all if fly/swim
1445         if (!(flags & (FL_FLY | FL_SWIM)))
1446         {
1447                 if (flags & FL_ONGROUND)
1448                 {
1449                         // freefall if onground and moving upward
1450                         // freefall if not standing on a world surface (it may be a lift)
1451                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1452                         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1453                         {
1454                                 ent->fields.server->flags -= FL_ONGROUND;
1455                                 SV_AddGravity(ent);
1456                                 SV_CheckVelocity(ent);
1457                                 SV_FlyMove(ent, sv.frametime, NULL);
1458                                 SV_LinkEdict(ent, true);
1459                         }
1460                 }
1461                 else
1462                 {
1463                         // freefall if not onground
1464                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1465
1466                         SV_AddGravity(ent);
1467                         SV_CheckVelocity(ent);
1468                         SV_FlyMove(ent, sv.frametime, NULL);
1469                         SV_LinkEdict(ent, true);
1470
1471                         // just hit ground
1472                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1473                                 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1474                 }
1475         }
1476
1477 // regular thinking
1478         SV_RunThink(ent);
1479
1480         SV_CheckWaterTransition(ent);
1481 }
1482
1483 //============================================================================
1484
1485 static void SV_Physics_Entity (prvm_edict_t *ent)
1486 {
1487         // don't run a move on newly spawned projectiles as it messes up movement
1488         // interpolation and rocket trails
1489         qboolean runmove = ent->priv.server->move;
1490         ent->priv.server->move = true;
1491         switch ((int) ent->fields.server->movetype)
1492         {
1493         case MOVETYPE_PUSH:
1494         case MOVETYPE_FAKEPUSH:
1495                 SV_Physics_Pusher (ent);
1496                 break;
1497         case MOVETYPE_NONE:
1498                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1499                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1500                         SV_RunThink (ent);
1501                 break;
1502         case MOVETYPE_FOLLOW:
1503                 SV_Physics_Follow (ent);
1504                 break;
1505         case MOVETYPE_NOCLIP:
1506                 if (SV_RunThink(ent))
1507                 {
1508                         SV_CheckWater(ent);
1509                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1510                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1511                 }
1512                 SV_LinkEdict(ent, false);
1513                 break;
1514         case MOVETYPE_STEP:
1515                 SV_Physics_Step (ent);
1516                 break;
1517         case MOVETYPE_WALK:
1518                 if (SV_RunThink (ent))
1519                 {
1520                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1521                                 SV_AddGravity (ent);
1522                         SV_CheckStuck (ent);
1523                         SV_WalkMove (ent);
1524                         SV_LinkEdict (ent, true);
1525                 }
1526                 break;
1527         case MOVETYPE_TOSS:
1528         case MOVETYPE_BOUNCE:
1529         case MOVETYPE_BOUNCEMISSILE:
1530         case MOVETYPE_FLYMISSILE:
1531         case MOVETYPE_FLY:
1532                 // regular thinking
1533                 if (SV_RunThink (ent) && runmove)
1534                         SV_Physics_Toss (ent);
1535                 break;
1536         default:
1537                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1538                 break;
1539         }
1540 }
1541
1542 void SV_ApplyClientMove (void);
1543 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1544 {
1545         SV_ApplyClientMove();
1546         // make sure the velocity is sane (not a NaN)
1547         SV_CheckVelocity(ent);
1548         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1549         if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1550         {
1551                 prog->globals.server->time = sv.time;
1552                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1553                 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1554         }
1555         else
1556                 SV_ClientThink ();
1557         // make sure the velocity is sane (not a NaN)
1558         SV_CheckVelocity(ent);
1559         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1560         // player_run/player_stand1 does not horribly malfunction if the
1561         // velocity becomes a number that is both == 0 and != 0
1562         // (sounds to me like NaN but to be absolutely safe...)
1563         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1564                 VectorClear(ent->fields.server->velocity);
1565         // call standard client pre-think
1566         prog->globals.server->time = sv.time;
1567         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1568         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1569         SV_CheckVelocity (ent);
1570
1571         switch ((int) ent->fields.server->movetype)
1572         {
1573         case MOVETYPE_PUSH:
1574         case MOVETYPE_FAKEPUSH:
1575                 SV_Physics_Pusher (ent);
1576                 break;
1577         case MOVETYPE_NONE:
1578                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1579                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1580                         SV_RunThink (ent);
1581                 break;
1582         case MOVETYPE_FOLLOW:
1583                 SV_Physics_Follow (ent);
1584                 break;
1585         case MOVETYPE_NOCLIP:
1586                 if (SV_RunThink(ent))
1587                 {
1588                         SV_CheckWater(ent);
1589                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1590                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1591                 }
1592                 break;
1593         case MOVETYPE_STEP:
1594                 SV_Physics_Step (ent);
1595                 break;
1596         case MOVETYPE_WALK:
1597                 if (SV_RunThink (ent))
1598                 {
1599                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1600                                 SV_AddGravity (ent);
1601                         SV_CheckStuck (ent);
1602                         SV_WalkMove (ent);
1603                 }
1604                 break;
1605         case MOVETYPE_TOSS:
1606         case MOVETYPE_BOUNCE:
1607         case MOVETYPE_BOUNCEMISSILE:
1608         case MOVETYPE_FLYMISSILE:
1609                 // regular thinking
1610                 if (SV_RunThink (ent))
1611                         SV_Physics_Toss (ent);
1612                 break;
1613         case MOVETYPE_FLY:
1614                 if (SV_RunThink (ent))
1615                 {
1616                         SV_CheckWater (ent);
1617                         SV_WalkMove (ent);
1618                 }
1619                 break;
1620         default:
1621                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1622                 break;
1623         }
1624
1625         SV_CheckVelocity (ent);
1626
1627         // call standard player post-think
1628         SV_LinkEdict (ent, true);
1629
1630         SV_CheckVelocity (ent);
1631
1632         prog->globals.server->time = sv.time;
1633         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1634         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1635 }
1636
1637 /*
1638 ================
1639 SV_Physics
1640
1641 ================
1642 */
1643 void SV_Physics (void)
1644 {
1645         int i;
1646         prvm_edict_t *ent;
1647
1648 // let the progs know that a new frame has started
1649         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1650         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1651         prog->globals.server->time = sv.time;
1652         prog->globals.server->frametime = sv.frametime;
1653         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1654
1655 //
1656 // treat each object in turn
1657 //
1658
1659         // if force_retouch, relink all the entities
1660         if (prog->globals.server->force_retouch > 0)
1661                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1662                         if (!ent->priv.server->free)
1663                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
1664
1665         // run physics on the client entities
1666         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1667         {
1668                 if (!ent->priv.server->free)
1669                 {
1670                         // don't do physics on disconnected clients, FrikBot relies on this
1671                         if (!host_client->spawned)
1672                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1673                         // don't run physics here if running asynchronously
1674                         else if (host_client->clmovement_skipphysicsframes > 0)
1675                                 host_client->clmovement_skipphysicsframes--;
1676                         else
1677                                 SV_Physics_ClientEntity(ent);
1678                 }
1679         }
1680
1681         // run physics on all the non-client entities
1682         if (!sv_freezenonclients.integer)
1683                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1684                         if (!ent->priv.server->free)
1685                                 SV_Physics_Entity(ent);
1686
1687         if (prog->globals.server->force_retouch > 0)
1688                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1689
1690         // LordHavoc: endframe support
1691         if (EndFrameQC)
1692         {
1693                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1694                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1695                 prog->globals.server->time = sv.time;
1696                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1697         }
1698
1699         // decrement prog->num_edicts if the highest number entities died
1700         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1701
1702         if (!sv_freezenonclients.integer)
1703                 sv.time += sv.frametime;
1704 }
1705
1706
1707 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1708 {
1709         int i;
1710         float gravity;
1711         vec3_t move, end;
1712         vec3_t original_origin;
1713         vec3_t original_velocity;
1714         vec3_t original_angles;
1715         vec3_t original_avelocity;
1716         prvm_eval_t *val;
1717         trace_t trace;
1718
1719         VectorCopy(tossent->fields.server->origin   , original_origin   );
1720         VectorCopy(tossent->fields.server->velocity , original_velocity );
1721         VectorCopy(tossent->fields.server->angles   , original_angles   );
1722         VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1723
1724         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1725         if (val != NULL && val->_float != 0)
1726                 gravity = val->_float;
1727         else
1728                 gravity = 1.0;
1729         gravity *= sv_gravity.value * 0.05;
1730
1731         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1732         {
1733                 SV_CheckVelocity (tossent);
1734                 tossent->fields.server->velocity[2] -= gravity;
1735                 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1736                 VectorScale (tossent->fields.server->velocity, 0.05, move);
1737                 VectorAdd (tossent->fields.server->origin, move, end);
1738                 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1739                 VectorCopy (trace.endpos, tossent->fields.server->origin);
1740
1741                 if (trace.fraction < 1)
1742                         break;
1743         }
1744
1745         VectorCopy(original_origin   , tossent->fields.server->origin   );
1746         VectorCopy(original_velocity , tossent->fields.server->velocity );
1747         VectorCopy(original_angles   , tossent->fields.server->angles   );
1748         VectorCopy(original_avelocity, tossent->fields.server->avelocity);
1749
1750         return trace;
1751 }
1752