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