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