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