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