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