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