*** empty log message ***
[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
24 /*
25
26
27 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.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4"};
43 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100"};
44 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800"};
45 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000"};
46 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0"};
47 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18"};
48 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "1"};
49 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1"};
50 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "1"};
51
52 #define MOVE_EPSILON    0.01
53
54 void SV_Physics_Toss (edict_t *ent);
55
56 void SV_Phys_Init (void)
57 {
58         Cvar_RegisterVariable(&sv_stepheight);
59         Cvar_RegisterVariable(&sv_jumpstep);
60         Cvar_RegisterVariable(&sv_wallfriction);
61         Cvar_RegisterVariable(&sv_newflymove);
62 }
63
64 /*
65 ================
66 SV_CheckAllEnts
67 ================
68 */
69 void SV_CheckAllEnts (void)
70 {
71         int e;
72         edict_t *check;
73
74         // see if any solid entities are inside the final position
75         check = NEXT_EDICT(sv.edicts);
76         for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
77         {
78                 if (check->e->free)
79                         continue;
80                 if (check->v->movetype == MOVETYPE_PUSH
81                  || check->v->movetype == MOVETYPE_NONE
82                  || check->v->movetype == MOVETYPE_FOLLOW
83                  || check->v->movetype == MOVETYPE_NOCLIP)
84                         continue;
85
86                 if (SV_TestEntityPosition (check))
87                         Con_Printf ("entity in invalid position\n");
88         }
89 }
90
91 /*
92 ================
93 SV_CheckVelocity
94 ================
95 */
96 void SV_CheckVelocity (edict_t *ent)
97 {
98         int i;
99         float wishspeed;
100
101 //
102 // bound velocity
103 //
104         for (i=0 ; i<3 ; i++)
105         {
106                 if (IS_NAN(ent->v->velocity[i]))
107                 {
108                         Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v->classname));
109                         ent->v->velocity[i] = 0;
110                 }
111                 if (IS_NAN(ent->v->origin[i]))
112                 {
113                         Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v->classname));
114                         ent->v->origin[i] = 0;
115                 }
116         }
117
118         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
119         wishspeed = DotProduct(ent->v->velocity, ent->v->velocity);
120         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
121         {
122                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
123                 ent->v->velocity[0] *= wishspeed;
124                 ent->v->velocity[1] *= wishspeed;
125                 ent->v->velocity[2] *= wishspeed;
126         }
127 }
128
129 /*
130 =============
131 SV_RunThink
132
133 Runs thinking code if time.  There is some play in the exact time the think
134 function will be called, because it is called before any movement is done
135 in a frame.  Not used for pushmove objects, because they must be exact.
136 Returns false if the entity removed itself.
137 =============
138 */
139 qboolean SV_RunThink (edict_t *ent)
140 {
141         float thinktime;
142
143         thinktime = ent->v->nextthink;
144         if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
145                 return true;
146
147         // don't let things stay in the past.
148         // it is possible to start that way by a trigger with a local time.
149         if (thinktime < sv.time)
150                 thinktime = sv.time;
151
152         ent->v->nextthink = 0;
153         pr_global_struct->time = thinktime;
154         pr_global_struct->self = EDICT_TO_PROG(ent);
155         pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
156         PR_ExecuteProgram (ent->v->think, "NULL think function");
157         return !ent->e->free;
158 }
159
160 /*
161 ==================
162 SV_Impact
163
164 Two entities have touched, so run their touch functions
165 ==================
166 */
167 void SV_Impact (edict_t *e1, edict_t *e2)
168 {
169         int old_self, old_other;
170
171         old_self = pr_global_struct->self;
172         old_other = pr_global_struct->other;
173
174         pr_global_struct->time = sv.time;
175         if (e1->v->touch && e1->v->solid != SOLID_NOT)
176         {
177                 pr_global_struct->self = EDICT_TO_PROG(e1);
178                 pr_global_struct->other = EDICT_TO_PROG(e2);
179                 PR_ExecuteProgram (e1->v->touch, "");
180         }
181
182         if (e2->v->touch && e2->v->solid != SOLID_NOT)
183         {
184                 pr_global_struct->self = EDICT_TO_PROG(e2);
185                 pr_global_struct->other = EDICT_TO_PROG(e1);
186                 PR_ExecuteProgram (e2->v->touch, "");
187         }
188
189         pr_global_struct->self = old_self;
190         pr_global_struct->other = old_other;
191 }
192
193
194 /*
195 ==================
196 ClipVelocity
197
198 Slide off of the impacting object
199 returns the blocked flags (1 = floor, 2 = step / wall)
200 ==================
201 */
202 #define STOP_EPSILON 0.1
203 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
204 {
205         int i;
206         float backoff;
207
208         backoff = -DotProduct (in, normal) * overbounce;
209         VectorMA(in, backoff, normal, out);
210
211         for (i = 0;i < 3;i++)
212                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
213                         out[i] = 0;
214 }
215
216
217 /*
218 ============
219 SV_FlyMove
220
221 The basic solid body movement clip that slides along multiple planes
222 Returns the clipflags if the velocity was modified (hit something solid)
223 1 = floor
224 2 = wall / step
225 4 = dead stop
226 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
227 ============
228 */
229 // LordHavoc: increased from 5 to 20
230 #define MAX_CLIP_PLANES 20
231 int SV_FlyMove (edict_t *ent, float time, float *stepnormal)
232 {
233         if (sv_newflymove.integer)
234         {
235                 int blocked, impact, bumpcount;
236                 vec3_t end, primal_velocity;
237                 trace_t trace;
238
239                 blocked = 0;
240                 VectorCopy (ent->v->velocity, primal_velocity);
241
242                 for (bumpcount = 0;bumpcount < 4;bumpcount++)
243                 {
244                         if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
245                                 break;
246
247                         VectorMA(ent->v->origin, time, ent->v->velocity, end);
248                         trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
249                         //Con_Printf("trace %f %f %f : %f : %f %f %f\n", trace.endpos[0], trace.endpos[1], trace.endpos[2], trace.fraction, trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
250
251                         /*
252                         if (trace.startsolid)
253                         {
254                                 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
255                                 // entity is trapped in another solid
256                                 VectorClear(ent->v->velocity);
257                                 return 3;
258                         }
259                         */
260
261                         if (trace.fraction > 0)
262                         {
263                                 // actually covered some distance
264                                 VectorCopy (trace.endpos, ent->v->origin);
265                         }
266
267                         // break if it moved the entire distance
268                         if (trace.fraction == 1)
269                                 break;
270
271                         if (!trace.ent)
272                                 Host_Error ("SV_FlyMove: !trace.ent");
273
274                         if ((int) ent->v->flags & FL_ONGROUND)
275                         {
276                                 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
277                                         impact = false;
278                                 else
279                                 {
280                                         ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
281                                         impact = true;
282                                 }
283                         }
284                         else
285                                 impact = true;
286
287                         if (trace.plane.normal[2] > 0.7)
288                         {
289                                 // floor
290                                 blocked |= 1;
291                                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
292                                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
293                         }
294                         if (!trace.plane.normal[2])
295                         {
296                                 // step
297                                 blocked |= 2;
298                                 // save the trace for player extrafriction
299                                 if (stepnormal)
300                                         VectorCopy(trace.plane.normal, stepnormal);
301                         }
302
303                         // run the impact function
304                         if (impact)
305                         {
306                                 SV_Impact (ent, trace.ent);
307
308                                 // break if removed by the impact function
309                                 if (ent->e->free)
310                                         break;
311                         }
312
313                         time *= 1 - trace.fraction;
314
315                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1);
316                 }
317
318                 return blocked;
319         }
320         else
321         {
322                 int i, j, blocked, impact, numplanes, bumpcount, numbumps;
323                 float d, time_left;
324                 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
325                 trace_t trace;
326
327                 numbumps = 4;
328
329                 blocked = 0;
330                 VectorCopy (ent->v->velocity, original_velocity);
331                 VectorCopy (ent->v->velocity, primal_velocity);
332                 numplanes = 0;
333
334                 time_left = time;
335
336                 for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
337                 {
338                         if (!ent->v->velocity[0] && !ent->v->velocity[1] && !ent->v->velocity[2])
339                                 break;
340
341                         for (i=0 ; i<3 ; i++)
342                                 end[i] = ent->v->origin[i] + time_left * ent->v->velocity[i];
343
344                         trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
345
346                         /*
347                         if (trace.startsolid)
348                         {
349                                 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
350                                 // entity is trapped in another solid
351                                 VectorClear(ent->v->velocity);
352                                 return 3;
353                         }
354                         */
355
356                         if (trace.fraction > 0)
357                         {
358                                 // actually covered some distance
359                                 VectorCopy (trace.endpos, ent->v->origin);
360                                 VectorCopy (ent->v->velocity, original_velocity);
361                                 numplanes = 0;
362                         }
363
364                         // break if it moved the entire distance
365                         if (trace.fraction == 1)
366                                 break;
367
368                         if (!trace.ent)
369                                 Host_Error ("SV_FlyMove: !trace.ent");
370
371                         if ((int) ent->v->flags & FL_ONGROUND)
372                         {
373                                 if (ent->v->groundentity == EDICT_TO_PROG(trace.ent))
374                                         impact = false;
375                                 else
376                                 {
377                                         ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
378                                         impact = true;
379                                 }
380                         }
381                         else
382                                 impact = true;
383
384                         if (trace.plane.normal[2] > 0.7)
385                         {
386                                 // floor
387                                 blocked |= 1;
388                                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
389                                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
390                         }
391                         if (!trace.plane.normal[2])
392                         {
393                                 // step
394                                 blocked |= 2;
395                                 // save the trace for player extrafriction
396                                 if (stepnormal)
397                                         VectorCopy(trace.plane.normal, stepnormal);
398                         }
399
400                         // run the impact function
401                         if (impact)
402                         {
403                                 SV_Impact (ent, trace.ent);
404
405                                 // break if removed by the impact function
406                                 if (ent->e->free)
407                                         break;
408                         }
409
410
411                         time_left -= time_left * trace.fraction;
412
413                         // clipped to another plane
414                         if (numplanes >= MAX_CLIP_PLANES)
415                         {
416                                 // this shouldn't really happen
417                                 VectorClear(ent->v->velocity);
418                                 return 3;
419                         }
420
421                         VectorCopy (trace.plane.normal, planes[numplanes]);
422                         numplanes++;
423
424                         // modify original_velocity so it parallels all of the clip planes
425                         for (i=0 ; i<numplanes ; i++)
426                         {
427                                 ClipVelocity (original_velocity, planes[i], new_velocity, 1);
428                                 for (j=0 ; j<numplanes ; j++)
429                                         if (j != i)
430                                         {
431                                                 // not ok
432                                                 if (DotProduct (new_velocity, planes[j]) < 0)
433                                                         break;
434                                         }
435                                 if (j == numplanes)
436                                         break;
437                         }
438
439                         if (i != numplanes)
440                         {
441                                 // go along this plane
442                                 VectorCopy (new_velocity, ent->v->velocity);
443                         }
444                         else
445                         {
446                                 // go along the crease
447                                 if (numplanes != 2)
448                                 {
449                                         VectorClear(ent->v->velocity);
450                                         return 7;
451                                 }
452                                 CrossProduct (planes[0], planes[1], dir);
453                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
454                                 VectorNormalize(dir);
455                                 d = DotProduct (dir, ent->v->velocity);
456                                 VectorScale (dir, d, ent->v->velocity);
457                         }
458
459                         // if original velocity is against the original velocity,
460                         // stop dead to avoid tiny occilations in sloping corners
461                         if (DotProduct (ent->v->velocity, primal_velocity) <= 0)
462                         {
463                                 VectorClear(ent->v->velocity);
464                                 return blocked;
465                         }
466                 }
467
468                 return blocked;
469         }
470 }
471
472
473 /*
474 ============
475 SV_AddGravity
476
477 ============
478 */
479 void SV_AddGravity (edict_t *ent)
480 {
481         float ent_gravity;
482         eval_t *val;
483
484         val = GETEDICTFIELDVALUE(ent, eval_gravity);
485         if (val!=0 && val->_float)
486                 ent_gravity = val->_float;
487         else
488                 ent_gravity = 1.0;
489         ent->v->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
490 }
491
492
493 /*
494 ===============================================================================
495
496 PUSHMOVE
497
498 ===============================================================================
499 */
500
501 /*
502 ============
503 SV_PushEntity
504
505 Does not change the entities velocity at all
506 ============
507 */
508 trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles)
509 {
510         trace_t trace;
511         vec3_t end;
512
513         VectorAdd (ent->v->origin, push, end);
514
515         if (ent->v->movetype == MOVETYPE_FLYMISSILE)
516                 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_MISSILE, ent);
517         else if (ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_NOT)
518                 // only clip against bmodels
519                 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NOMONSTERS, ent);
520         else
521                 trace = SV_Move (ent->v->origin, ent->v->mins, ent->v->maxs, end, MOVE_NORMAL, ent);
522
523         VectorCopy (trace.endpos, ent->v->origin);
524         // FIXME: turn players specially
525         ent->v->angles[1] += trace.fraction * pushangles[1];
526         SV_LinkEdict (ent, true);
527
528         if (trace.fraction < 1 && trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent)))
529                 SV_Impact (ent, trace.ent);
530         return trace;
531 }
532
533
534 /*
535 ============
536 SV_PushMove
537
538 ============
539 */
540 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
541 void SV_PushMove (edict_t *pusher, float movetime)
542 {
543         int i, e, index;
544         edict_t *check, *ed;
545         float savesolid, movetime2, pushltime;
546         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org, org2;
547         int num_moved;
548         model_t *pushermodel;
549         trace_t trace;
550
551         switch ((int) pusher->v->solid)
552         {
553         // LordHavoc: valid pusher types
554         case SOLID_BSP:
555         case SOLID_BBOX:
556         case SOLID_SLIDEBOX:
557         case SOLID_CORPSE: // LordHavoc: this would be weird...
558                 break;
559         // LordHavoc: no collisions
560         case SOLID_NOT:
561         case SOLID_TRIGGER:
562                 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
563                 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
564                 pusher->v->ltime += movetime;
565                 SV_LinkEdict (pusher, false);
566                 return;
567         default:
568                 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
569         }
570         if (!pusher->v->velocity[0] && !pusher->v->velocity[1] && !pusher->v->velocity[2] && !pusher->v->avelocity[0] && !pusher->v->avelocity[1] && !pusher->v->avelocity[2])
571         {
572                 pusher->v->ltime += movetime;
573                 return;
574         }
575         index = (int) pusher->v->modelindex;
576         if (index < 1 || index >= MAX_MODELS)
577                 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
578         pushermodel = sv.models[index];
579
580         movetime2 = movetime;
581         VectorScale(pusher->v->velocity, movetime2, move1);
582         VectorScale(pusher->v->avelocity, movetime2, moveangle);
583         if (moveangle[0] || moveangle[2])
584         {
585                 for (i = 0;i < 3;i++)
586                 {
587                         if (move1[i] > 0)
588                         {
589                                 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
590                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
591                         }
592                         else
593                         {
594                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
595                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
596                         }
597                 }
598         }
599         else if (moveangle[1])
600         {
601                 for (i = 0;i < 3;i++)
602                 {
603                         if (move1[i] > 0)
604                         {
605                                 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
606                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
607                         }
608                         else
609                         {
610                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
611                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
612                         }
613                 }
614         }
615         else
616         {
617                 for (i = 0;i < 3;i++)
618                 {
619                         if (move1[i] > 0)
620                         {
621                                 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
622                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
623                         }
624                         else
625                         {
626                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
627                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
628                         }
629                 }
630         }
631
632         VectorNegate (moveangle, a);
633         AngleVectorsFLU (a, forward, left, up);
634
635         VectorCopy (pusher->v->origin, pushorig);
636         VectorCopy (pusher->v->angles, pushang);
637         pushltime = pusher->v->ltime;
638
639 // move the pusher to it's final position
640
641         VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
642         VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
643         pusher->v->ltime += movetime;
644         SV_LinkEdict (pusher, false);
645
646         savesolid = pusher->v->solid;
647
648 // see if any solid entities are inside the final position
649         num_moved = 0;
650         check = NEXT_EDICT(sv.edicts);
651         for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
652         {
653                 if (check->e->free)
654                         continue;
655                 if (check->v->movetype == MOVETYPE_PUSH
656                  || check->v->movetype == MOVETYPE_NONE
657                  || check->v->movetype == MOVETYPE_FOLLOW
658                  || check->v->movetype == MOVETYPE_NOCLIP)
659                         continue;
660
661                 // if the entity is standing on the pusher, it will definitely be moved
662                 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
663                 {
664                         if (check->v->absmin[0] >= maxs[0]
665                          || check->v->absmax[0] <= mins[0]
666                          || check->v->absmin[1] >= maxs[1]
667                          || check->v->absmax[1] <= mins[1]
668                          || check->v->absmin[2] >= maxs[2]
669                          || check->v->absmax[2] <= mins[2])
670                                 continue;
671
672                         trace = SV_ClipMoveToEntity (pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin);
673                         if (!trace.startsolid)
674                                 continue;
675                 }
676
677                 if (forward[0] < 0.999f) // quick way to check if any rotation is used
678                 {
679                         VectorSubtract (check->v->origin, pusher->v->origin, org);
680                         org2[0] = DotProduct (org, forward);
681                         org2[1] = DotProduct (org, left);
682                         org2[2] = DotProduct (org, up);
683                         VectorSubtract (org2, org, move);
684                         VectorAdd (move, move1, move);
685                 }
686                 else
687                         VectorCopy (move1, move);
688
689                 // remove the onground flag for non-players
690                 if (check->v->movetype != MOVETYPE_WALK)
691                         check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
692
693                 VectorCopy (check->v->origin, check->e->moved_from);
694                 VectorCopy (check->v->angles, check->e->moved_fromangles);
695                 sv.moved_edicts[num_moved++] = check;
696
697                 // try moving the contacted entity
698                 pusher->v->solid = SOLID_NOT;
699                 trace = SV_PushEntity (check, move, moveangle);
700                 pusher->v->solid = savesolid; // was SOLID_BSP
701
702                 // if it is still inside the pusher, block
703                 if (SV_TestEntityPosition (check))
704                 {
705                         // try moving the contacted entity a tiny bit further to account for precision errors
706                         pusher->v->solid = SOLID_NOT;
707                         VectorScale(move, 0.1, move);
708                         trace = SV_PushEntity (check, move, vec3_origin);
709                         pusher->v->solid = savesolid;
710                         if (SV_TestEntityPosition (check))
711                         {
712                                 // still inside pusher, so it's really blocked
713
714                                 // fail the move
715                                 if (check->v->mins[0] == check->v->maxs[0])
716                                         continue;
717                                 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
718                                 {
719                                         // corpse
720                                         check->v->mins[0] = check->v->mins[1] = 0;
721                                         VectorCopy (check->v->mins, check->v->maxs);
722                                         continue;
723                                 }
724
725                                 VectorCopy (pushorig, pusher->v->origin);
726                                 VectorCopy (pushang, pusher->v->angles);
727                                 pusher->v->ltime = pushltime;
728                                 SV_LinkEdict (pusher, false);
729
730                                 // move back any entities we already moved
731                                 for (i = 0;i < num_moved;i++)
732                                 {
733                                         ed = sv.moved_edicts[i];
734                                         VectorCopy (ed->e->moved_from, ed->v->origin);
735                                         VectorCopy (ed->e->moved_fromangles, ed->v->angles);
736                                         SV_LinkEdict (ed, false);
737                                 }
738
739                                 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
740                                 if (pusher->v->blocked)
741                                 {
742                                         pr_global_struct->self = EDICT_TO_PROG(pusher);
743                                         pr_global_struct->other = EDICT_TO_PROG(check);
744                                         PR_ExecuteProgram (pusher->v->blocked, "");
745                                 }
746                                 return;
747                         }
748                 }
749         }
750 }
751
752 /*
753 ================
754 SV_Physics_Pusher
755
756 ================
757 */
758 void SV_Physics_Pusher (edict_t *ent)
759 {
760         float thinktime, oldltime, movetime;
761
762         oldltime = ent->v->ltime;
763
764         thinktime = ent->v->nextthink;
765         if (thinktime < ent->v->ltime + sv.frametime)
766         {
767                 movetime = thinktime - ent->v->ltime;
768                 if (movetime < 0)
769                         movetime = 0;
770         }
771         else
772                 movetime = sv.frametime;
773
774         if (movetime)
775                 // advances ent->v->ltime if not blocked
776                 SV_PushMove (ent, movetime);
777
778         if (thinktime > oldltime && thinktime <= ent->v->ltime)
779         {
780                 ent->v->nextthink = 0;
781                 pr_global_struct->time = sv.time;
782                 pr_global_struct->self = EDICT_TO_PROG(ent);
783                 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
784                 PR_ExecuteProgram (ent->v->think, "NULL think function");
785         }
786 }
787
788
789 /*
790 ===============================================================================
791
792 CLIENT MOVEMENT
793
794 ===============================================================================
795 */
796
797 /*
798 =============
799 SV_CheckStuck
800
801 This is a big hack to try and fix the rare case of getting stuck in the world
802 clipping hull.
803 =============
804 */
805 void SV_CheckStuck (edict_t *ent)
806 {
807         int i, j, z;
808         vec3_t org;
809
810         if (!SV_TestEntityPosition(ent))
811         {
812                 VectorCopy (ent->v->origin, ent->v->oldorigin);
813                 return;
814         }
815
816         VectorCopy (ent->v->origin, org);
817         VectorCopy (ent->v->oldorigin, ent->v->origin);
818         if (!SV_TestEntityPosition(ent))
819         {
820                 Con_DPrintf ("Unstuck.\n");
821                 SV_LinkEdict (ent, true);
822                 return;
823         }
824
825         for (z=0 ; z< 18 ; z++)
826                 for (i=-1 ; i <= 1 ; i++)
827                         for (j=-1 ; j <= 1 ; j++)
828                         {
829                                 ent->v->origin[0] = org[0] + i;
830                                 ent->v->origin[1] = org[1] + j;
831                                 ent->v->origin[2] = org[2] + z;
832                                 if (!SV_TestEntityPosition(ent))
833                                 {
834                                         Con_DPrintf ("Unstuck.\n");
835                                         SV_LinkEdict (ent, true);
836                                         return;
837                                 }
838                         }
839
840         VectorCopy (org, ent->v->origin);
841         Con_DPrintf ("player is stuck.\n");
842 }
843
844
845 /*
846 =============
847 SV_CheckWater
848 =============
849 */
850 qboolean SV_CheckWater (edict_t *ent)
851 {
852         int cont;
853         vec3_t point;
854
855         point[0] = ent->v->origin[0];
856         point[1] = ent->v->origin[1];
857         point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
858
859         ent->v->waterlevel = 0;
860         ent->v->watertype = CONTENTS_EMPTY;
861         cont = SV_PointQ1Contents(point);
862         if (cont <= CONTENTS_WATER)
863         {
864                 ent->v->watertype = cont;
865                 ent->v->waterlevel = 1;
866                 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
867                 cont = SV_PointQ1Contents(point);
868                 if (cont <= CONTENTS_WATER)
869                 {
870                         ent->v->waterlevel = 2;
871                         point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
872                         cont = SV_PointQ1Contents(point);
873                         if (cont <= CONTENTS_WATER)
874                                 ent->v->waterlevel = 3;
875                 }
876         }
877
878         return ent->v->waterlevel > 1;
879 }
880
881 /*
882 ============
883 SV_WallFriction
884
885 ============
886 */
887 void SV_WallFriction (edict_t *ent, float *stepnormal)
888 {
889         float d, i;
890         vec3_t forward, into, side;
891
892         AngleVectors (ent->v->v_angle, forward, NULL, NULL);
893         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
894         {
895                 // cut the tangential velocity
896                 i = DotProduct (stepnormal, ent->v->velocity);
897                 VectorScale (stepnormal, i, into);
898                 VectorSubtract (ent->v->velocity, into, side);
899                 ent->v->velocity[0] = side[0] * (1 + d);
900                 ent->v->velocity[1] = side[1] * (1 + d);
901         }
902 }
903
904 /*
905 =====================
906 SV_TryUnstick
907
908 Player has come to a dead stop, possibly due to the problem with limited
909 float precision at some angle joins in the BSP hull.
910
911 Try fixing by pushing one pixel in each direction.
912
913 This is a hack, but in the interest of good gameplay...
914 ======================
915 */
916 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
917 {
918         int i, clip;
919         vec3_t oldorg, dir;
920
921         VectorCopy (ent->v->origin, oldorg);
922         VectorClear (dir);
923
924         for (i=0 ; i<8 ; i++)
925         {
926                 // try pushing a little in an axial direction
927                 switch (i)
928                 {
929                         case 0: dir[0] = 2; dir[1] = 0; break;
930                         case 1: dir[0] = 0; dir[1] = 2; break;
931                         case 2: dir[0] = -2; dir[1] = 0; break;
932                         case 3: dir[0] = 0; dir[1] = -2; break;
933                         case 4: dir[0] = 2; dir[1] = 2; break;
934                         case 5: dir[0] = -2; dir[1] = 2; break;
935                         case 6: dir[0] = 2; dir[1] = -2; break;
936                         case 7: dir[0] = -2; dir[1] = -2; break;
937                 }
938
939                 SV_PushEntity (ent, dir, vec3_origin);
940
941                 // retry the original move
942                 ent->v->velocity[0] = oldvel[0];
943                 ent->v->velocity[1] = oldvel[1];
944                 ent->v->velocity[2] = 0;
945                 clip = SV_FlyMove (ent, 0.1, NULL);
946
947                 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
948                  || fabs(oldorg[0] - ent->v->origin[0]) > 4)
949                         return clip;
950
951                 // go back to the original pos and try again
952                 VectorCopy (oldorg, ent->v->origin);
953         }
954
955         // still not moving
956         VectorClear (ent->v->velocity);
957         return 7;
958 }
959
960 /*
961 =====================
962 SV_WalkMove
963
964 Only used by players
965 ======================
966 */
967 void SV_WalkMove (edict_t *ent)
968 {
969         int clip, oldonground;
970         vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
971         trace_t downtrace;
972
973         SV_CheckVelocity(ent);
974
975         // do a regular slide move unless it looks like you ran into a step
976         oldonground = (int)ent->v->flags & FL_ONGROUND;
977         ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
978
979         VectorCopy (ent->v->origin, oldorg);
980         VectorCopy (ent->v->velocity, oldvel);
981
982         clip = SV_FlyMove (ent, sv.frametime, NULL);
983
984         SV_CheckVelocity(ent);
985
986         // if move didn't block on a step, return
987         if ( !(clip & 2) )
988                 return;
989
990         if (ent->v->movetype != MOVETYPE_FLY)
991         {
992                 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
993                         // don't stair up while jumping
994                         return;
995
996                 if (ent->v->movetype != MOVETYPE_WALK)
997                         // gibbed by a trigger
998                         return;
999         }
1000
1001         SV_CheckVelocity(ent);
1002
1003         if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1004                 return;
1005
1006         VectorCopy (ent->v->origin, nosteporg);
1007         VectorCopy (ent->v->velocity, nostepvel);
1008
1009         // try moving up and forward to go up a step
1010         // back to start pos
1011         VectorCopy (oldorg, ent->v->origin);
1012
1013         VectorClear (upmove);
1014         VectorClear (downmove);
1015         upmove[2] = sv_stepheight.value;
1016         downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1017
1018         // move up
1019         // FIXME: don't link?
1020         SV_PushEntity (ent, upmove, vec3_origin);
1021
1022         // move forward
1023         ent->v->velocity[0] = oldvel[0];
1024         ent->v->velocity[1] = oldvel[1];
1025         ent->v->velocity[2] = 0;
1026         clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1027         ent->v->velocity[2] += oldvel[2];
1028
1029         // check for stuckness, possibly due to the limited precision of floats
1030         // in the clipping hulls
1031         if (clip
1032          && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1033          && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1034                 // stepping up didn't make any progress
1035                 clip = SV_TryUnstick (ent, oldvel);
1036
1037         // extra friction based on view angle
1038         if (clip & 2 && sv_wallfriction.integer)
1039                 SV_WallFriction (ent, stepnormal);
1040
1041         // move down
1042         // FIXME: don't link?
1043         downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1044
1045         if (downtrace.plane.normal[2] > 0.7)
1046         {
1047                 // LordHavoc: disabled this so you can walk on monsters/players
1048                 //if (ent->v->solid == SOLID_BSP)
1049                 {
1050                         ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1051                         ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1052                 }
1053         }
1054         else
1055         {
1056                 // if the push down didn't end up on good ground, use the move without
1057                 // the step up.  This happens near wall / slope combinations, and can
1058                 // cause the player to hop up higher on a slope too steep to climb
1059                 VectorCopy (nosteporg, ent->v->origin);
1060                 VectorCopy (nostepvel, ent->v->velocity);
1061         }
1062
1063         SV_CheckVelocity(ent);
1064 }
1065
1066 //============================================================================
1067
1068 /*
1069 =============
1070 SV_Physics_Follow
1071
1072 Entities that are "stuck" to another entity
1073 =============
1074 */
1075 void SV_Physics_Follow (edict_t *ent)
1076 {
1077         vec3_t vf, vr, vu, angles, v;
1078         edict_t *e;
1079
1080         // regular thinking
1081         if (!SV_RunThink (ent))
1082                 return;
1083
1084         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1085         e = PROG_TO_EDICT(ent->v->aiment);
1086         if (e->v->angles[0] == ent->v->punchangle[0] && e->v->angles[1] == ent->v->punchangle[1] && e->v->angles[2] == ent->v->punchangle[2])
1087         {
1088                 // quick case for no rotation
1089                 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1090         }
1091         else
1092         {
1093                 angles[0] = -ent->v->punchangle[0];
1094                 angles[1] =  ent->v->punchangle[1];
1095                 angles[2] =  ent->v->punchangle[2];
1096                 AngleVectors (angles, vf, vr, vu);
1097                 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1098                 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1099                 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1100                 angles[0] = -e->v->angles[0];
1101                 angles[1] =  e->v->angles[1];
1102                 angles[2] =  e->v->angles[2];
1103                 AngleVectors (angles, vf, vr, vu);
1104                 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1105                 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1106                 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1107         }
1108         VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1109         SV_LinkEdict (ent, true);
1110 }
1111
1112 /*
1113 ==============================================================================
1114
1115 TOSS / BOUNCE
1116
1117 ==============================================================================
1118 */
1119
1120 /*
1121 =============
1122 SV_CheckWaterTransition
1123
1124 =============
1125 */
1126 void SV_CheckWaterTransition (edict_t *ent)
1127 {
1128         int cont;
1129         cont = SV_PointQ1Contents(ent->v->origin);
1130         if (!ent->v->watertype)
1131         {
1132                 // just spawned here
1133                 ent->v->watertype = cont;
1134                 ent->v->waterlevel = 1;
1135                 return;
1136         }
1137
1138         // check if the entity crossed into or out of water
1139         if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1140                 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1141
1142         if (cont <= CONTENTS_WATER)
1143         {
1144                 ent->v->watertype = cont;
1145                 ent->v->waterlevel = 1;
1146         }
1147         else
1148         {
1149                 ent->v->watertype = CONTENTS_EMPTY;
1150                 ent->v->waterlevel = 0;
1151         }
1152 }
1153
1154 /*
1155 =============
1156 SV_Physics_Toss
1157
1158 Toss, bounce, and fly movement.  When onground, do nothing.
1159 =============
1160 */
1161 void SV_Physics_Toss (edict_t *ent)
1162 {
1163         trace_t trace;
1164         vec3_t move;
1165         edict_t *groundentity;
1166
1167         // regular thinking
1168         if (!SV_RunThink (ent))
1169                 return;
1170
1171 // if onground, return without moving
1172         if ((int)ent->v->flags & FL_ONGROUND)
1173         {
1174                 if (ent->v->groundentity == 0)
1175                         return;
1176                 // if ent was supported by a brush model on previous frame,
1177                 // and groundentity is now freed, set groundentity to 0 (floating)
1178                 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1179                 if (groundentity->v->solid == SOLID_BSP)
1180                 {
1181                         ent->e->suspendedinairflag = true;
1182                         return;
1183                 }
1184                 else if (ent->e->suspendedinairflag && groundentity->e->free)
1185                 {
1186                         // leave it suspended in the air
1187                         ent->v->groundentity = 0;
1188                         ent->e->suspendedinairflag = false;
1189                         return;
1190                 }
1191         }
1192         ent->e->suspendedinairflag = false;
1193
1194         SV_CheckVelocity (ent);
1195
1196 // add gravity
1197         if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1198                 SV_AddGravity (ent);
1199
1200 // move angles
1201         VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1202
1203 // move origin
1204         VectorScale (ent->v->velocity, sv.frametime, move);
1205         trace = SV_PushEntity (ent, move, vec3_origin);
1206         if (ent->e->free)
1207                 return;
1208
1209         if (trace.fraction < 1)
1210         {
1211                 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1212                 {
1213                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1214                         ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1215                 }
1216                 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1217                 {
1218                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1219                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1220                         if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1221                         {
1222                                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1223                                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1224                                 VectorClear (ent->v->velocity);
1225                                 VectorClear (ent->v->avelocity);
1226                         }
1227                         else
1228                                 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1229                 }
1230                 else
1231                 {
1232                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1233                         if (trace.plane.normal[2] > 0.7)
1234                         {
1235                                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1236                                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1237                                 VectorClear (ent->v->velocity);
1238                                 VectorClear (ent->v->avelocity);
1239                         }
1240                         else
1241                                 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1242                 }
1243         }
1244
1245 // check for in water
1246         SV_CheckWaterTransition (ent);
1247 }
1248
1249 /*
1250 ===============================================================================
1251
1252 STEPPING MOVEMENT
1253
1254 ===============================================================================
1255 */
1256
1257 /*
1258 =============
1259 SV_Physics_Step
1260
1261 Monsters freefall when they don't have a ground entity, otherwise
1262 all movement is done with discrete steps.
1263
1264 This is also used for objects that have become still on the ground, but
1265 will fall if the floor is pulled out from under them.
1266 =============
1267 */
1268 void SV_Physics_Step (edict_t *ent)
1269 {
1270         // freefall if not onground/fly/swim
1271         if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1272         {
1273                 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1274
1275                 SV_AddGravity(ent);
1276                 SV_CheckVelocity(ent);
1277                 SV_FlyMove(ent, sv.frametime, NULL);
1278                 SV_LinkEdict(ent, false);
1279
1280                 // just hit ground
1281                 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1282                         SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1283         }
1284
1285 // regular thinking
1286         SV_RunThink(ent);
1287
1288         SV_CheckWaterTransition(ent);
1289 }
1290
1291 //============================================================================
1292
1293 /*
1294 ================
1295 SV_Physics
1296
1297 ================
1298 */
1299 void SV_Physics (void)
1300 {
1301         int i;
1302         edict_t *ent;
1303
1304 // let the progs know that a new frame has started
1305         pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1306         pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1307         pr_global_struct->time = sv.time;
1308         PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1309
1310 //
1311 // treat each object in turn
1312 //
1313         ent = sv.edicts;
1314         for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1315         {
1316                 if (ent->e->free)
1317                         continue;
1318
1319                 if (pr_global_struct->force_retouch)
1320                         SV_LinkEdict (ent, true);       // force retouch even for stationary
1321
1322                 if (i > 0 && i <= svs.maxclients)
1323                 {
1324                         if (!svs.clients[i-1].spawned)
1325                                 continue;
1326                         // connected slot
1327                         // call standard client pre-think
1328                         SV_CheckVelocity (ent);
1329                         pr_global_struct->time = sv.time;
1330                         pr_global_struct->self = EDICT_TO_PROG(ent);
1331                         PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1332                         SV_CheckVelocity (ent);
1333                 }
1334
1335                 // LordHavoc: merged client and normal entity physics
1336                 switch ((int) ent->v->movetype)
1337                 {
1338                 case MOVETYPE_PUSH:
1339                         SV_Physics_Pusher (ent);
1340                         break;
1341                 case MOVETYPE_NONE:
1342                         // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1343                         if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1344                                 SV_RunThink (ent);
1345                         break;
1346                 case MOVETYPE_FOLLOW:
1347                         SV_Physics_Follow (ent);
1348                         break;
1349                 case MOVETYPE_NOCLIP:
1350                         if (SV_RunThink(ent))
1351                         {
1352                                 SV_CheckWater(ent);
1353                                 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1354                                 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1355                         }
1356                         // relink normal entities here, players always get relinked so don't relink twice
1357                         if (!(i > 0 && i <= svs.maxclients))
1358                                 SV_LinkEdict(ent, false);
1359                         break;
1360                 case MOVETYPE_STEP:
1361                         SV_Physics_Step (ent);
1362                         break;
1363                 case MOVETYPE_WALK:
1364                         if (SV_RunThink (ent))
1365                         {
1366                                 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1367                                         SV_AddGravity (ent);
1368                                 SV_CheckStuck (ent);
1369                                 SV_WalkMove (ent);
1370                                 SV_LinkEdict (ent, true);
1371                         }
1372                         break;
1373                 case MOVETYPE_TOSS:
1374                 case MOVETYPE_BOUNCE:
1375                 case MOVETYPE_BOUNCEMISSILE:
1376                 case MOVETYPE_FLYMISSILE:
1377                         SV_Physics_Toss (ent);
1378                         break;
1379                 case MOVETYPE_FLY:
1380                         if (i > 0 && i <= svs.maxclients)
1381                         {
1382                                 if (SV_RunThink (ent))
1383                                 {
1384                                         SV_CheckWater (ent);
1385                                         SV_WalkMove (ent);
1386                                 }
1387                         }
1388                         else
1389                                 SV_Physics_Toss (ent);
1390                         break;
1391                 default:
1392                         Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1393                         break;
1394                 }
1395
1396                 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1397                 {
1398                         SV_CheckVelocity (ent);
1399
1400                         // call standard player post-think
1401                         SV_LinkEdict (ent, true);
1402
1403                         SV_CheckVelocity (ent);
1404
1405                         pr_global_struct->time = sv.time;
1406                         pr_global_struct->self = EDICT_TO_PROG(ent);
1407                         PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1408                 }
1409         }
1410
1411         if (pr_global_struct->force_retouch)
1412                 pr_global_struct->force_retouch--;
1413
1414         // LordHavoc: endframe support
1415         if (EndFrameQC)
1416         {
1417                 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1418                 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1419                 pr_global_struct->time = sv.time;
1420                 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1421         }
1422
1423         sv.time += sv.frametime;
1424 }
1425
1426
1427 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1428 {
1429         int i;
1430         float gravity, savesolid;
1431         vec3_t move, end;
1432         edict_t tempent, *tent;
1433         entvars_t vars;
1434         eval_t *val;
1435         trace_t trace;
1436
1437         // copy the vars over
1438         memcpy(&vars, tossent->v, sizeof(entvars_t));
1439         // set up the temp entity to point to the copied vars
1440         tent = &tempent;
1441         tent->v = &vars;
1442
1443         savesolid = tossent->v->solid;
1444         tossent->v->solid = SOLID_NOT;
1445
1446         // this has to fetch the field from the original edict, since our copy is truncated
1447         val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1448         if (val != NULL && val->_float != 0)
1449                 gravity = val->_float;
1450         else
1451                 gravity = 1.0;
1452         gravity *= sv_gravity.value * 0.05;
1453
1454         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1455         {
1456                 SV_CheckVelocity (tent);
1457                 tent->v->velocity[2] -= gravity;
1458                 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1459                 VectorScale (tent->v->velocity, 0.05, move);
1460                 VectorAdd (tent->v->origin, move, end);
1461                 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1462                 VectorCopy (trace.endpos, tent->v->origin);
1463
1464                 if (trace.fraction < 1 && trace.ent)
1465                         if (trace.ent != ignore)
1466                                 break;
1467         }
1468         tossent->v->solid = savesolid;
1469         trace.fraction = 0; // not relevant
1470         return trace;
1471 }
1472