]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_phys.c
INCOMPLETE SDL video support (don't use it!) No input, no multiple video
[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.001)
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.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
550         switch ((int) pusher->v->solid)
551         {
552         // LordHavoc: valid pusher types
553         case SOLID_BSP:
554         case SOLID_BBOX:
555         case SOLID_SLIDEBOX:
556         case SOLID_CORPSE: // LordHavoc: this would be weird...
557                 break;
558         // LordHavoc: no collisions
559         case SOLID_NOT:
560         case SOLID_TRIGGER:
561                 VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
562                 VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
563                 pusher->v->ltime += movetime;
564                 SV_LinkEdict (pusher, false);
565                 return;
566         default:
567                 Host_Error("SV_PushMove: unrecognized solid type %f\n", pusher->v->solid);
568         }
569         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])
570         {
571                 pusher->v->ltime += movetime;
572                 return;
573         }
574         index = (int) pusher->v->modelindex;
575         if (index < 1 || index >= MAX_MODELS)
576                 Host_Error("SV_PushMove: invalid modelindex %f\n", pusher->v->modelindex);
577         pushermodel = sv.models[index];
578
579         movetime2 = movetime;
580         VectorScale(pusher->v->velocity, movetime2, move1);
581         VectorScale(pusher->v->avelocity, movetime2, moveangle);
582         if (moveangle[0] || moveangle[2])
583         {
584                 for (i = 0;i < 3;i++)
585                 {
586                         if (move1[i] > 0)
587                         {
588                                 mins[i] = pushermodel->rotatedmins[i] + pusher->v->origin[i] - 1;
589                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
590                         }
591                         else
592                         {
593                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->v->origin[i] - 1;
594                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->v->origin[i] + 1;
595                         }
596                 }
597         }
598         else if (moveangle[1])
599         {
600                 for (i = 0;i < 3;i++)
601                 {
602                         if (move1[i] > 0)
603                         {
604                                 mins[i] = pushermodel->yawmins[i] + pusher->v->origin[i] - 1;
605                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
606                         }
607                         else
608                         {
609                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->v->origin[i] - 1;
610                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->v->origin[i] + 1;
611                         }
612                 }
613         }
614         else
615         {
616                 for (i = 0;i < 3;i++)
617                 {
618                         if (move1[i] > 0)
619                         {
620                                 mins[i] = pushermodel->normalmins[i] + pusher->v->origin[i] - 1;
621                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->v->origin[i] + 1;
622                         }
623                         else
624                         {
625                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->v->origin[i] - 1;
626                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->v->origin[i] + 1;
627                         }
628                 }
629         }
630
631         VectorNegate (moveangle, a);
632         AngleVectorsFLU (a, forward, left, up);
633
634         VectorCopy (pusher->v->origin, pushorig);
635         VectorCopy (pusher->v->angles, pushang);
636         pushltime = pusher->v->ltime;
637
638 // move the pusher to it's final position
639
640         VectorMA (pusher->v->origin, movetime, pusher->v->velocity, pusher->v->origin);
641         VectorMA (pusher->v->angles, movetime, pusher->v->avelocity, pusher->v->angles);
642         pusher->v->ltime += movetime;
643         SV_LinkEdict (pusher, false);
644
645         savesolid = pusher->v->solid;
646
647 // see if any solid entities are inside the final position
648         num_moved = 0;
649         check = NEXT_EDICT(sv.edicts);
650         for (e = 1;e < sv.num_edicts;e++, check = NEXT_EDICT(check))
651         {
652                 if (check->e->free)
653                         continue;
654                 if (check->v->movetype == MOVETYPE_PUSH
655                  || check->v->movetype == MOVETYPE_NONE
656                  || check->v->movetype == MOVETYPE_FOLLOW
657                  || check->v->movetype == MOVETYPE_NOCLIP)
658                         continue;
659
660                 // if the entity is standing on the pusher, it will definitely be moved
661                 if (!(((int)check->v->flags & FL_ONGROUND) && PROG_TO_EDICT(check->v->groundentity) == pusher))
662                 {
663                         if (check->v->absmin[0] >= maxs[0]
664                          || check->v->absmax[0] <= mins[0]
665                          || check->v->absmin[1] >= maxs[1]
666                          || check->v->absmax[1] <= mins[1]
667                          || check->v->absmin[2] >= maxs[2]
668                          || check->v->absmax[2] <= mins[2])
669                                 continue;
670
671                         if (!SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
672                                 continue;
673                 }
674
675                 if (forward[0] != 1) // quick way to check if any rotation is used
676                 {
677                         VectorSubtract (check->v->origin, pusher->v->origin, org);
678                         org2[0] = DotProduct (org, forward);
679                         org2[1] = DotProduct (org, left);
680                         org2[2] = DotProduct (org, up);
681                         VectorSubtract (org2, org, move);
682                         VectorAdd (move, move1, move);
683                 }
684                 else
685                         VectorCopy (move1, move);
686
687                 // remove the onground flag for non-players
688                 if (check->v->movetype != MOVETYPE_WALK)
689                         check->v->flags = (int)check->v->flags & ~FL_ONGROUND;
690
691                 VectorCopy (check->v->origin, check->e->moved_from);
692                 VectorCopy (check->v->angles, check->e->moved_fromangles);
693                 sv.moved_edicts[num_moved++] = check;
694
695                 // try moving the contacted entity
696                 pusher->v->solid = SOLID_NOT;
697                 SV_PushEntity (check, move, moveangle);
698                 pusher->v->solid = savesolid; // was SOLID_BSP
699
700                 // if it is still inside the pusher, block
701                 if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
702                 {
703                         // try moving the contacted entity a tiny bit further to account for precision errors
704                         pusher->v->solid = SOLID_NOT;
705                         VectorScale(move, 0.1, move);
706                         SV_PushEntity (check, move, vec3_origin);
707                         pusher->v->solid = savesolid;
708                         if (SV_ClipMoveToEntity(pusher, check->v->origin, check->v->mins, check->v->maxs, check->v->origin).startsolid)
709                         {
710                                 // still inside pusher, so it's really blocked
711
712                                 // fail the move
713                                 if (check->v->mins[0] == check->v->maxs[0])
714                                         continue;
715                                 if (check->v->solid == SOLID_NOT || check->v->solid == SOLID_TRIGGER)
716                                 {
717                                         // corpse
718                                         check->v->mins[0] = check->v->mins[1] = 0;
719                                         VectorCopy (check->v->mins, check->v->maxs);
720                                         continue;
721                                 }
722
723                                 VectorCopy (pushorig, pusher->v->origin);
724                                 VectorCopy (pushang, pusher->v->angles);
725                                 pusher->v->ltime = pushltime;
726                                 SV_LinkEdict (pusher, false);
727
728                                 // move back any entities we already moved
729                                 for (i = 0;i < num_moved;i++)
730                                 {
731                                         ed = sv.moved_edicts[i];
732                                         VectorCopy (ed->e->moved_from, ed->v->origin);
733                                         VectorCopy (ed->e->moved_fromangles, ed->v->angles);
734                                         SV_LinkEdict (ed, false);
735                                 }
736
737                                 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
738                                 if (pusher->v->blocked)
739                                 {
740                                         pr_global_struct->self = EDICT_TO_PROG(pusher);
741                                         pr_global_struct->other = EDICT_TO_PROG(check);
742                                         PR_ExecuteProgram (pusher->v->blocked, "");
743                                 }
744                                 return;
745                         }
746                 }
747         }
748 }
749
750 /*
751 ================
752 SV_Physics_Pusher
753
754 ================
755 */
756 void SV_Physics_Pusher (edict_t *ent)
757 {
758         float thinktime, oldltime, movetime;
759
760         oldltime = ent->v->ltime;
761
762         thinktime = ent->v->nextthink;
763         if (thinktime < ent->v->ltime + sv.frametime)
764         {
765                 movetime = thinktime - ent->v->ltime;
766                 if (movetime < 0)
767                         movetime = 0;
768         }
769         else
770                 movetime = sv.frametime;
771
772         if (movetime)
773                 // advances ent->v->ltime if not blocked
774                 SV_PushMove (ent, movetime);
775
776         if (thinktime > oldltime && thinktime <= ent->v->ltime)
777         {
778                 ent->v->nextthink = 0;
779                 pr_global_struct->time = sv.time;
780                 pr_global_struct->self = EDICT_TO_PROG(ent);
781                 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
782                 PR_ExecuteProgram (ent->v->think, "NULL think function");
783         }
784 }
785
786
787 /*
788 ===============================================================================
789
790 CLIENT MOVEMENT
791
792 ===============================================================================
793 */
794
795 /*
796 =============
797 SV_CheckStuck
798
799 This is a big hack to try and fix the rare case of getting stuck in the world
800 clipping hull.
801 =============
802 */
803 void SV_CheckStuck (edict_t *ent)
804 {
805         int i, j, z;
806         vec3_t org;
807
808         if (!SV_TestEntityPosition(ent))
809         {
810                 VectorCopy (ent->v->origin, ent->v->oldorigin);
811                 return;
812         }
813
814         VectorCopy (ent->v->origin, org);
815         VectorCopy (ent->v->oldorigin, ent->v->origin);
816         if (!SV_TestEntityPosition(ent))
817         {
818                 Con_DPrintf ("Unstuck.\n");
819                 SV_LinkEdict (ent, true);
820                 return;
821         }
822
823         for (z=0 ; z< 18 ; z++)
824                 for (i=-1 ; i <= 1 ; i++)
825                         for (j=-1 ; j <= 1 ; j++)
826                         {
827                                 ent->v->origin[0] = org[0] + i;
828                                 ent->v->origin[1] = org[1] + j;
829                                 ent->v->origin[2] = org[2] + z;
830                                 if (!SV_TestEntityPosition(ent))
831                                 {
832                                         Con_DPrintf ("Unstuck.\n");
833                                         SV_LinkEdict (ent, true);
834                                         return;
835                                 }
836                         }
837
838         VectorCopy (org, ent->v->origin);
839         Con_DPrintf ("player is stuck.\n");
840 }
841
842
843 /*
844 =============
845 SV_CheckWater
846 =============
847 */
848 qboolean SV_CheckWater (edict_t *ent)
849 {
850         int cont;
851         vec3_t point;
852
853         point[0] = ent->v->origin[0];
854         point[1] = ent->v->origin[1];
855         point[2] = ent->v->origin[2] + ent->v->mins[2] + 1;
856
857         ent->v->waterlevel = 0;
858         ent->v->watertype = CONTENTS_EMPTY;
859         cont = SV_PointQ1Contents(point);
860         if (cont <= CONTENTS_WATER)
861         {
862                 ent->v->watertype = cont;
863                 ent->v->waterlevel = 1;
864                 point[2] = ent->v->origin[2] + (ent->v->mins[2] + ent->v->maxs[2])*0.5;
865                 cont = SV_PointQ1Contents(point);
866                 if (cont <= CONTENTS_WATER)
867                 {
868                         ent->v->waterlevel = 2;
869                         point[2] = ent->v->origin[2] + ent->v->view_ofs[2];
870                         cont = SV_PointQ1Contents(point);
871                         if (cont <= CONTENTS_WATER)
872                                 ent->v->waterlevel = 3;
873                 }
874         }
875
876         return ent->v->waterlevel > 1;
877 }
878
879 /*
880 ============
881 SV_WallFriction
882
883 ============
884 */
885 void SV_WallFriction (edict_t *ent, float *stepnormal)
886 {
887         float d, i;
888         vec3_t forward, into, side;
889
890         AngleVectors (ent->v->v_angle, forward, NULL, NULL);
891         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
892         {
893                 // cut the tangential velocity
894                 i = DotProduct (stepnormal, ent->v->velocity);
895                 VectorScale (stepnormal, i, into);
896                 VectorSubtract (ent->v->velocity, into, side);
897                 ent->v->velocity[0] = side[0] * (1 + d);
898                 ent->v->velocity[1] = side[1] * (1 + d);
899         }
900 }
901
902 /*
903 =====================
904 SV_TryUnstick
905
906 Player has come to a dead stop, possibly due to the problem with limited
907 float precision at some angle joins in the BSP hull.
908
909 Try fixing by pushing one pixel in each direction.
910
911 This is a hack, but in the interest of good gameplay...
912 ======================
913 */
914 int SV_TryUnstick (edict_t *ent, vec3_t oldvel)
915 {
916         int i, clip;
917         vec3_t oldorg, dir;
918
919         VectorCopy (ent->v->origin, oldorg);
920         VectorClear (dir);
921
922         for (i=0 ; i<8 ; i++)
923         {
924                 // try pushing a little in an axial direction
925                 switch (i)
926                 {
927                         case 0: dir[0] = 2; dir[1] = 0; break;
928                         case 1: dir[0] = 0; dir[1] = 2; break;
929                         case 2: dir[0] = -2; dir[1] = 0; break;
930                         case 3: dir[0] = 0; dir[1] = -2; break;
931                         case 4: dir[0] = 2; dir[1] = 2; break;
932                         case 5: dir[0] = -2; dir[1] = 2; break;
933                         case 6: dir[0] = 2; dir[1] = -2; break;
934                         case 7: dir[0] = -2; dir[1] = -2; break;
935                 }
936
937                 SV_PushEntity (ent, dir, vec3_origin);
938
939                 // retry the original move
940                 ent->v->velocity[0] = oldvel[0];
941                 ent->v->velocity[1] = oldvel[1];
942                 ent->v->velocity[2] = 0;
943                 clip = SV_FlyMove (ent, 0.1, NULL);
944
945                 if (fabs(oldorg[1] - ent->v->origin[1]) > 4
946                  || fabs(oldorg[0] - ent->v->origin[0]) > 4)
947                 {
948                         Con_DPrintf("TryUnstick - success.\n");
949                         return clip;
950                 }
951
952                 // go back to the original pos and try again
953                 VectorCopy (oldorg, ent->v->origin);
954         }
955
956         // still not moving
957         VectorClear (ent->v->velocity);
958         Con_DPrintf("TryUnstick - failure.\n");
959         return 7;
960 }
961
962 /*
963 =====================
964 SV_WalkMove
965
966 Only used by players
967 ======================
968 */
969 void SV_WalkMove (edict_t *ent)
970 {
971         int clip, oldonground;
972         vec3_t upmove, downmove, oldorg, oldvel, nosteporg, nostepvel, stepnormal;
973         trace_t downtrace;
974
975         SV_CheckVelocity(ent);
976
977         // do a regular slide move unless it looks like you ran into a step
978         oldonground = (int)ent->v->flags & FL_ONGROUND;
979         ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
980
981         VectorCopy (ent->v->origin, oldorg);
982         VectorCopy (ent->v->velocity, oldvel);
983
984         clip = SV_FlyMove (ent, sv.frametime, NULL);
985
986         SV_CheckVelocity(ent);
987
988         // if move didn't block on a step, return
989         if ( !(clip & 2) )
990                 return;
991
992         if (ent->v->movetype != MOVETYPE_FLY)
993         {
994                 if (!oldonground && ent->v->waterlevel == 0 && !sv_jumpstep.integer)
995                         // don't stair up while jumping
996                         return;
997
998                 if (ent->v->movetype != MOVETYPE_WALK)
999                         // gibbed by a trigger
1000                         return;
1001         }
1002
1003         SV_CheckVelocity(ent);
1004
1005         if (sv_nostep.integer || (int)ent->v->flags & FL_WATERJUMP )
1006                 return;
1007
1008         VectorCopy (ent->v->origin, nosteporg);
1009         VectorCopy (ent->v->velocity, nostepvel);
1010
1011         // try moving up and forward to go up a step
1012         // back to start pos
1013         VectorCopy (oldorg, ent->v->origin);
1014
1015         VectorClear (upmove);
1016         VectorClear (downmove);
1017         upmove[2] = sv_stepheight.value;
1018         downmove[2] = -sv_stepheight.value + oldvel[2]*sv.frametime;
1019
1020         // move up
1021         // FIXME: don't link?
1022         SV_PushEntity (ent, upmove, vec3_origin);
1023
1024         // move forward
1025         ent->v->velocity[0] = oldvel[0];
1026         ent->v->velocity[1] = oldvel[1];
1027         ent->v->velocity[2] = 0;
1028         clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1029         ent->v->velocity[2] += oldvel[2];
1030
1031         // check for stuckness, possibly due to the limited precision of floats
1032         // in the clipping hulls
1033         if (clip
1034          && fabs(oldorg[1] - ent->v->origin[1]) < 0.03125
1035          && fabs(oldorg[0] - ent->v->origin[0]) < 0.03125)
1036                 // stepping up didn't make any progress
1037                 clip = SV_TryUnstick (ent, oldvel);
1038
1039         // extra friction based on view angle
1040         if (clip & 2 && sv_wallfriction.integer)
1041                 SV_WallFriction (ent, stepnormal);
1042
1043         // move down
1044         // FIXME: don't link?
1045         downtrace = SV_PushEntity (ent, downmove, vec3_origin);
1046
1047         if (downtrace.plane.normal[2] > 0.7)
1048         {
1049                 // LordHavoc: disabled this so you can walk on monsters/players
1050                 //if (ent->v->solid == SOLID_BSP)
1051                 {
1052                         ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1053                         ent->v->groundentity = EDICT_TO_PROG(downtrace.ent);
1054                 }
1055         }
1056         else
1057         {
1058                 // if the push down didn't end up on good ground, use the move without
1059                 // the step up.  This happens near wall / slope combinations, and can
1060                 // cause the player to hop up higher on a slope too steep to climb
1061                 VectorCopy (nosteporg, ent->v->origin);
1062                 VectorCopy (nostepvel, ent->v->velocity);
1063         }
1064
1065         SV_CheckVelocity(ent);
1066 }
1067
1068 //============================================================================
1069
1070 /*
1071 =============
1072 SV_Physics_Follow
1073
1074 Entities that are "stuck" to another entity
1075 =============
1076 */
1077 void SV_Physics_Follow (edict_t *ent)
1078 {
1079         vec3_t vf, vr, vu, angles, v;
1080         edict_t *e;
1081
1082         // regular thinking
1083         if (!SV_RunThink (ent))
1084                 return;
1085
1086         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1087         e = PROG_TO_EDICT(ent->v->aiment);
1088         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])
1089         {
1090                 // quick case for no rotation
1091                 VectorAdd(e->v->origin, ent->v->view_ofs, ent->v->origin);
1092         }
1093         else
1094         {
1095                 angles[0] = -ent->v->punchangle[0];
1096                 angles[1] =  ent->v->punchangle[1];
1097                 angles[2] =  ent->v->punchangle[2];
1098                 AngleVectors (angles, vf, vr, vu);
1099                 v[0] = ent->v->view_ofs[0] * vf[0] + ent->v->view_ofs[1] * vr[0] + ent->v->view_ofs[2] * vu[0];
1100                 v[1] = ent->v->view_ofs[0] * vf[1] + ent->v->view_ofs[1] * vr[1] + ent->v->view_ofs[2] * vu[1];
1101                 v[2] = ent->v->view_ofs[0] * vf[2] + ent->v->view_ofs[1] * vr[2] + ent->v->view_ofs[2] * vu[2];
1102                 angles[0] = -e->v->angles[0];
1103                 angles[1] =  e->v->angles[1];
1104                 angles[2] =  e->v->angles[2];
1105                 AngleVectors (angles, vf, vr, vu);
1106                 ent->v->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->v->origin[0];
1107                 ent->v->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->v->origin[1];
1108                 ent->v->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->v->origin[2];
1109         }
1110         VectorAdd (e->v->angles, ent->v->v_angle, ent->v->angles);
1111         SV_LinkEdict (ent, true);
1112 }
1113
1114 /*
1115 ==============================================================================
1116
1117 TOSS / BOUNCE
1118
1119 ==============================================================================
1120 */
1121
1122 /*
1123 =============
1124 SV_CheckWaterTransition
1125
1126 =============
1127 */
1128 void SV_CheckWaterTransition (edict_t *ent)
1129 {
1130         int cont;
1131         cont = SV_PointQ1Contents(ent->v->origin);
1132         if (!ent->v->watertype)
1133         {
1134                 // just spawned here
1135                 ent->v->watertype = cont;
1136                 ent->v->waterlevel = 1;
1137                 return;
1138         }
1139
1140         // check if the entity crossed into or out of water
1141         if ((ent->v->watertype == CONTENTS_WATER || ent->v->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME))
1142                 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1143
1144         if (cont <= CONTENTS_WATER)
1145         {
1146                 ent->v->watertype = cont;
1147                 ent->v->waterlevel = 1;
1148         }
1149         else
1150         {
1151                 ent->v->watertype = CONTENTS_EMPTY;
1152                 ent->v->waterlevel = 0;
1153         }
1154 }
1155
1156 /*
1157 =============
1158 SV_Physics_Toss
1159
1160 Toss, bounce, and fly movement.  When onground, do nothing.
1161 =============
1162 */
1163 void SV_Physics_Toss (edict_t *ent)
1164 {
1165         trace_t trace;
1166         vec3_t move;
1167         edict_t *groundentity;
1168
1169         // regular thinking
1170         if (!SV_RunThink (ent))
1171                 return;
1172
1173 // if onground, return without moving
1174         if ((int)ent->v->flags & FL_ONGROUND)
1175         {
1176                 if (ent->v->groundentity == 0)
1177                         return;
1178                 // if ent was supported by a brush model on previous frame,
1179                 // and groundentity is now freed, set groundentity to 0 (floating)
1180                 groundentity = PROG_TO_EDICT(ent->v->groundentity);
1181                 if (groundentity->v->solid == SOLID_BSP)
1182                 {
1183                         ent->e->suspendedinairflag = true;
1184                         return;
1185                 }
1186                 else if (ent->e->suspendedinairflag && groundentity->e->free)
1187                 {
1188                         // leave it suspended in the air
1189                         ent->v->groundentity = 0;
1190                         ent->e->suspendedinairflag = false;
1191                         return;
1192                 }
1193         }
1194         ent->e->suspendedinairflag = false;
1195
1196         SV_CheckVelocity (ent);
1197
1198 // add gravity
1199         if (ent->v->movetype == MOVETYPE_TOSS || ent->v->movetype == MOVETYPE_BOUNCE)
1200                 SV_AddGravity (ent);
1201
1202 // move angles
1203         VectorMA (ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1204
1205 // move origin
1206         VectorScale (ent->v->velocity, sv.frametime, move);
1207         trace = SV_PushEntity (ent, move, vec3_origin);
1208         if (ent->e->free)
1209                 return;
1210
1211         if (trace.fraction < 1)
1212         {
1213                 if (ent->v->movetype == MOVETYPE_BOUNCEMISSILE)
1214                 {
1215                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 2.0);
1216                         ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1217                 }
1218                 else if (ent->v->movetype == MOVETYPE_BOUNCE)
1219                 {
1220                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.5);
1221                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1222                         if (trace.plane.normal[2] > 0.7 && DotProduct(trace.plane.normal, ent->v->velocity) < 60)
1223                         {
1224                                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1225                                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1226                                 VectorClear (ent->v->velocity);
1227                                 VectorClear (ent->v->avelocity);
1228                         }
1229                         else
1230                                 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1231                 }
1232                 else
1233                 {
1234                         ClipVelocity (ent->v->velocity, trace.plane.normal, ent->v->velocity, 1.0);
1235                         if (trace.plane.normal[2] > 0.7)
1236                         {
1237                                 ent->v->flags = (int)ent->v->flags | FL_ONGROUND;
1238                                 ent->v->groundentity = EDICT_TO_PROG(trace.ent);
1239                                 VectorClear (ent->v->velocity);
1240                                 VectorClear (ent->v->avelocity);
1241                         }
1242                         else
1243                                 ent->v->flags = (int)ent->v->flags & ~FL_ONGROUND;
1244                 }
1245         }
1246
1247 // check for in water
1248         SV_CheckWaterTransition (ent);
1249 }
1250
1251 /*
1252 ===============================================================================
1253
1254 STEPPING MOVEMENT
1255
1256 ===============================================================================
1257 */
1258
1259 /*
1260 =============
1261 SV_Physics_Step
1262
1263 Monsters freefall when they don't have a ground entity, otherwise
1264 all movement is done with discrete steps.
1265
1266 This is also used for objects that have become still on the ground, but
1267 will fall if the floor is pulled out from under them.
1268 =============
1269 */
1270 void SV_Physics_Step (edict_t *ent)
1271 {
1272         // freefall if not onground/fly/swim
1273         if (!((int)ent->v->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1274         {
1275                 int hitsound = ent->v->velocity[2] < sv_gravity.value * -0.1;
1276
1277                 SV_AddGravity(ent);
1278                 SV_CheckVelocity(ent);
1279                 SV_FlyMove(ent, sv.frametime, NULL);
1280                 SV_LinkEdict(ent, true);
1281
1282                 // just hit ground
1283                 if (hitsound && (int)ent->v->flags & FL_ONGROUND)
1284                         SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1285         }
1286
1287 // regular thinking
1288         SV_RunThink(ent);
1289
1290         SV_CheckWaterTransition(ent);
1291 }
1292
1293 //============================================================================
1294
1295 /*
1296 ================
1297 SV_Physics
1298
1299 ================
1300 */
1301 void SV_Physics (void)
1302 {
1303         int i;
1304         edict_t *ent;
1305
1306 // let the progs know that a new frame has started
1307         pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1308         pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1309         pr_global_struct->time = sv.time;
1310         PR_ExecuteProgram (pr_global_struct->StartFrame, "QC function StartFrame is missing");
1311
1312 //
1313 // treat each object in turn
1314 //
1315         ent = sv.edicts;
1316         for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
1317         {
1318                 if (ent->e->free)
1319                         continue;
1320
1321                 if (pr_global_struct->force_retouch)
1322                         SV_LinkEdict (ent, true);       // force retouch even for stationary
1323
1324                 if (i > 0 && i <= svs.maxclients)
1325                 {
1326                         if (!svs.clients[i-1].spawned)
1327                                 continue;
1328                         // connected slot
1329                         // call standard client pre-think
1330                         SV_CheckVelocity (ent);
1331                         pr_global_struct->time = sv.time;
1332                         pr_global_struct->self = EDICT_TO_PROG(ent);
1333                         PR_ExecuteProgram (pr_global_struct->PlayerPreThink, "QC function PlayerPreThink is missing");
1334                         SV_CheckVelocity (ent);
1335                 }
1336
1337                 // LordHavoc: merged client and normal entity physics
1338                 switch ((int) ent->v->movetype)
1339                 {
1340                 case MOVETYPE_PUSH:
1341                         SV_Physics_Pusher (ent);
1342                         break;
1343                 case MOVETYPE_NONE:
1344                         // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1345                         if (ent->v->nextthink > 0 && ent->v->nextthink <= sv.time + sv.frametime)
1346                                 SV_RunThink (ent);
1347                         break;
1348                 case MOVETYPE_FOLLOW:
1349                         SV_Physics_Follow (ent);
1350                         break;
1351                 case MOVETYPE_NOCLIP:
1352                         if (SV_RunThink(ent))
1353                         {
1354                                 SV_CheckWater(ent);
1355                                 VectorMA(ent->v->origin, sv.frametime, ent->v->velocity, ent->v->origin);
1356                                 VectorMA(ent->v->angles, sv.frametime, ent->v->avelocity, ent->v->angles);
1357                         }
1358                         // relink normal entities here, players always get relinked so don't relink twice
1359                         if (!(i > 0 && i <= svs.maxclients))
1360                                 SV_LinkEdict(ent, false);
1361                         break;
1362                 case MOVETYPE_STEP:
1363                         SV_Physics_Step (ent);
1364                         break;
1365                 case MOVETYPE_WALK:
1366                         if (SV_RunThink (ent))
1367                         {
1368                                 if (!SV_CheckWater (ent) && ! ((int)ent->v->flags & FL_WATERJUMP) )
1369                                         SV_AddGravity (ent);
1370                                 SV_CheckStuck (ent);
1371                                 SV_WalkMove (ent);
1372                                 // relink normal entities here, players always get relinked so don't relink twice
1373                                 if (!(i > 0 && i <= svs.maxclients))
1374                                         SV_LinkEdict (ent, true);
1375                         }
1376                         break;
1377                 case MOVETYPE_TOSS:
1378                 case MOVETYPE_BOUNCE:
1379                 case MOVETYPE_BOUNCEMISSILE:
1380                 case MOVETYPE_FLYMISSILE:
1381                         SV_Physics_Toss (ent);
1382                         break;
1383                 case MOVETYPE_FLY:
1384                         if (i > 0 && i <= svs.maxclients)
1385                         {
1386                                 if (SV_RunThink (ent))
1387                                 {
1388                                         SV_CheckWater (ent);
1389                                         SV_WalkMove (ent);
1390                                 }
1391                         }
1392                         else
1393                                 SV_Physics_Toss (ent);
1394                         break;
1395                 default:
1396                         Host_Error ("SV_Physics: bad movetype %i", (int)ent->v->movetype);
1397                         break;
1398                 }
1399
1400                 if (i > 0 && i <= svs.maxclients && !ent->e->free)
1401                 {
1402                         SV_CheckVelocity (ent);
1403
1404                         // call standard player post-think
1405                         SV_LinkEdict (ent, true);
1406
1407                         SV_CheckVelocity (ent);
1408
1409                         pr_global_struct->time = sv.time;
1410                         pr_global_struct->self = EDICT_TO_PROG(ent);
1411                         PR_ExecuteProgram (pr_global_struct->PlayerPostThink, "QC function PlayerPostThink is missing");
1412                 }
1413         }
1414
1415         if (pr_global_struct->force_retouch)
1416                 pr_global_struct->force_retouch--;
1417
1418         // LordHavoc: endframe support
1419         if (EndFrameQC)
1420         {
1421                 pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
1422                 pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
1423                 pr_global_struct->time = sv.time;
1424                 PR_ExecuteProgram ((func_t)(EndFrameQC - pr_functions), "");
1425         }
1426
1427         sv.time += sv.frametime;
1428 }
1429
1430
1431 trace_t SV_Trace_Toss (edict_t *tossent, edict_t *ignore)
1432 {
1433         int i;
1434         float gravity, savesolid;
1435         vec3_t move, end;
1436         edict_t tempent, *tent;
1437         entvars_t vars;
1438         eval_t *val;
1439         trace_t trace;
1440
1441         // copy the vars over
1442         memcpy(&vars, tossent->v, sizeof(entvars_t));
1443         // set up the temp entity to point to the copied vars
1444         tent = &tempent;
1445         tent->v = &vars;
1446
1447         savesolid = tossent->v->solid;
1448         tossent->v->solid = SOLID_NOT;
1449
1450         // this has to fetch the field from the original edict, since our copy is truncated
1451         val = GETEDICTFIELDVALUE(tossent, eval_gravity);
1452         if (val != NULL && val->_float != 0)
1453                 gravity = val->_float;
1454         else
1455                 gravity = 1.0;
1456         gravity *= sv_gravity.value * 0.05;
1457
1458         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1459         {
1460                 SV_CheckVelocity (tent);
1461                 tent->v->velocity[2] -= gravity;
1462                 VectorMA (tent->v->angles, 0.05, tent->v->avelocity, tent->v->angles);
1463                 VectorScale (tent->v->velocity, 0.05, move);
1464                 VectorAdd (tent->v->origin, move, end);
1465                 trace = SV_Move (tent->v->origin, tent->v->mins, tent->v->maxs, end, MOVE_NORMAL, tent);
1466                 VectorCopy (trace.endpos, tent->v->origin);
1467
1468                 if (trace.fraction < 1 && trace.ent)
1469                         if (trace.ent != ignore)
1470                                 break;
1471         }
1472         tossent->v->solid = savesolid;
1473         trace.fraction = 0; // not relevant
1474         return trace;
1475 }
1476