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