changed PF_WARNING to not do a return
[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 // used only for VM_GetTempString
24 #include "prvm_cmds.h"
25
26 /*
27
28
29 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.
30
31 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
32
33 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
34 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
35 corpses are SOLID_NOT and MOVETYPE_TOSS
36 crates are SOLID_BBOX and MOVETYPE_TOSS
37 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
38 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
39
40 solid_edge items only clip against bsp models.
41
42 */
43
44 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
45 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
46 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
47 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
48 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
49 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
50 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
51 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
52 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
53 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
54 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
55 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
56
57 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
58 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
59
60 // TODO: move this extern to server.h
61 extern cvar_t sv_clmovement_waitforinput;
62
63 #define MOVE_EPSILON    0.01
64
65 void SV_Physics_Toss (prvm_edict_t *ent);
66
67 void SV_Phys_Init (void)
68 {
69         Cvar_RegisterVariable(&sv_stepheight);
70         Cvar_RegisterVariable(&sv_jumpstep);
71         Cvar_RegisterVariable(&sv_wallfriction);
72         Cvar_RegisterVariable(&sv_newflymove);
73         Cvar_RegisterVariable(&sv_freezenonclients);
74
75         Cvar_RegisterVariable(&sv_playerphysicsqc);
76
77         Cvar_RegisterVariable(&sv_sound_watersplash);
78         Cvar_RegisterVariable(&sv_sound_land);
79 }
80
81 /*
82 ============
83 SV_TestEntityPosition
84
85 returns true if the entity is in solid currently
86 ============
87 */
88 static int SV_TestEntityPosition (prvm_edict_t *ent)
89 {
90         trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
91         if (trace.startsupercontents & SUPERCONTENTS_SOLID)
92                 return true;
93         else
94                 return false;
95 }
96
97 /*
98 ================
99 SV_CheckAllEnts
100 ================
101 */
102 void SV_CheckAllEnts (void)
103 {
104         int e;
105         prvm_edict_t *check;
106
107         // see if any solid entities are inside the final position
108         check = PRVM_NEXT_EDICT(prog->edicts);
109         for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
110         {
111                 if (check->priv.server->free)
112                         continue;
113                 if (check->fields.server->movetype == MOVETYPE_PUSH
114                  || check->fields.server->movetype == MOVETYPE_NONE
115                  || check->fields.server->movetype == MOVETYPE_FOLLOW
116                  || check->fields.server->movetype == MOVETYPE_NOCLIP)
117                         continue;
118
119                 if (SV_TestEntityPosition (check))
120                         Con_Print("entity in invalid position\n");
121         }
122 }
123
124 /*
125 ================
126 SV_CheckVelocity
127 ================
128 */
129 void SV_CheckVelocity (prvm_edict_t *ent)
130 {
131         int i;
132         float wishspeed;
133
134 //
135 // bound velocity
136 //
137         for (i=0 ; i<3 ; i++)
138         {
139                 if (IS_NAN(ent->fields.server->velocity[i]))
140                 {
141                         Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
142                         ent->fields.server->velocity[i] = 0;
143                 }
144                 if (IS_NAN(ent->fields.server->origin[i]))
145                 {
146                         Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
147                         ent->fields.server->origin[i] = 0;
148                 }
149         }
150
151         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
152         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
153         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
154         {
155                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
156                 ent->fields.server->velocity[0] *= wishspeed;
157                 ent->fields.server->velocity[1] *= wishspeed;
158                 ent->fields.server->velocity[2] *= wishspeed;
159         }
160 }
161
162 /*
163 =============
164 SV_RunThink
165
166 Runs thinking code if time.  There is some play in the exact time the think
167 function will be called, because it is called before any movement is done
168 in a frame.  Not used for pushmove objects, because they must be exact.
169 Returns false if the entity removed itself.
170 =============
171 */
172 qboolean SV_RunThink (prvm_edict_t *ent)
173 {
174         float thinktime;
175
176         thinktime = ent->fields.server->nextthink;
177         if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
178                 return true;
179
180         // don't let things stay in the past.
181         // it is possible to start that way by a trigger with a local time.
182         if (thinktime < sv.time)
183                 thinktime = sv.time;
184
185         ent->fields.server->nextthink = 0;
186         prog->globals.server->time = thinktime;
187         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
188         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
189         PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
190         return !ent->priv.server->free;
191 }
192
193 /*
194 ==================
195 SV_Impact
196
197 Two entities have touched, so run their touch functions
198 ==================
199 */
200 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
201 {
202         int old_self, old_other;
203         prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
204         prvm_eval_t *val;
205
206         old_self = prog->globals.server->self;
207         old_other = prog->globals.server->other;
208
209         prog->globals.server->time = sv.time;
210         if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
211         {
212                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
213                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
214                 prog->globals.server->trace_allsolid = trace->allsolid;
215                 prog->globals.server->trace_startsolid = trace->startsolid;
216                 prog->globals.server->trace_fraction = trace->fraction;
217                 prog->globals.server->trace_inwater = trace->inwater;
218                 prog->globals.server->trace_inopen = trace->inopen;
219                 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
220                 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
221                 prog->globals.server->trace_plane_dist =  trace->plane.dist;
222                 if (trace->ent)
223                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
224                 else
225                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
226                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
227                         val->_float = trace->startsupercontents;
228                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
229                         val->_float = trace->hitsupercontents;
230                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
231                         val->_float = trace->hitq3surfaceflags;
232                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
233                 {
234                         if (trace->hittexture)
235                         {
236                                 char *s = VM_GetTempString();
237                                 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
238                                 val->string = PRVM_SetEngineString(s);
239                         }
240                         else
241                                 val->string = 0;
242                 }
243                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
244         }
245
246         if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
247         {
248                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
249                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
250                 prog->globals.server->trace_allsolid = false;
251                 prog->globals.server->trace_startsolid = false;
252                 prog->globals.server->trace_fraction = 1;
253                 prog->globals.server->trace_inwater = false;
254                 prog->globals.server->trace_inopen = true;
255                 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
256                 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
257                 prog->globals.server->trace_plane_dist = 0;
258                 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
259                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
260                         val->_float = 0;
261                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
262                         val->_float = 0;
263                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
264                         val->_float = 0;
265                 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
266                         val->string = 0;
267                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
268         }
269
270         prog->globals.server->self = old_self;
271         prog->globals.server->other = old_other;
272 }
273
274
275 /*
276 ==================
277 ClipVelocity
278
279 Slide off of the impacting object
280 returns the blocked flags (1 = floor, 2 = step / wall)
281 ==================
282 */
283 #define STOP_EPSILON 0.1
284 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
285 {
286         int i;
287         float backoff;
288
289         backoff = -DotProduct (in, normal) * overbounce;
290         VectorMA(in, backoff, normal, out);
291
292         for (i = 0;i < 3;i++)
293                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
294                         out[i] = 0;
295 }
296
297
298 /*
299 ============
300 SV_FlyMove
301
302 The basic solid body movement clip that slides along multiple planes
303 Returns the clipflags if the velocity was modified (hit something solid)
304 1 = floor
305 2 = wall / step
306 4 = dead stop
307 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
308 ============
309 */
310 // LordHavoc: increased from 5 to 32
311 #define MAX_CLIP_PLANES 32
312 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
313 {
314         int blocked, bumpcount;
315         int i, j, impact, numplanes;
316         float d, time_left;
317         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
318         trace_t trace;
319         blocked = 0;
320         VectorCopy(ent->fields.server->velocity, original_velocity);
321         VectorCopy(ent->fields.server->velocity, primal_velocity);
322         numplanes = 0;
323         time_left = time;
324         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
325         {
326                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
327                         break;
328
329                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
330                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
331 #if 0
332                 //if (trace.fraction < 0.002)
333                 {
334 #if 1
335                         vec3_t start;
336                         trace_t testtrace;
337                         VectorCopy(ent->fields.server->origin, start);
338                         start[2] += 3;//0.03125;
339                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
340                         end[2] += 3;//0.03125;
341                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
342                         if (trace.fraction < testtrace.fraction && !testtrace.startsolid && (testtrace.fraction == 1 || DotProduct(trace.plane.normal, ent->fields.server->velocity) < DotProduct(testtrace.plane.normal, ent->fields.server->velocity)))
343                         {
344                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
345                                 trace = testtrace;
346                         }
347 #endif
348 #if 0
349                         //j = -1;
350                         for (i = 0;i < numplanes;i++)
351                         {
352                                 VectorCopy(ent->fields.server->origin, start);
353                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
354                                 VectorMA(start, 3, planes[i], start);
355                                 VectorMA(end, 3, planes[i], end);
356                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
357                                 if (trace.fraction < testtrace.fraction)
358                                 {
359                                         trace = testtrace;
360                                         VectorCopy(start, ent->fields.server->origin);
361                                         //j = i;
362                                 }
363                         }
364                         //if (j >= 0)
365                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
366 #endif
367                 }
368 #endif
369
370 #if 0
371                 Con_Printf("entity %i bump %i: velocity %f %f %f trace %f", ent - prog->edicts, bumpcount, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2], trace.fraction);
372                 if (trace.fraction < 1)
373                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
374                 Con_Print("\n");
375 #endif
376
377                 if (trace.bmodelstartsolid)
378                 {
379                         // LordHavoc: note: this code is what makes entities stick in place
380                         // if embedded in world only (you can walk through other objects if
381                         // stuck)
382                         // entity is trapped in another solid
383                         VectorClear(ent->fields.server->velocity);
384                         return 3;
385                 }
386
387                 // break if it moved the entire distance
388                 if (trace.fraction == 1)
389                 {
390                         VectorCopy(trace.endpos, ent->fields.server->origin);
391                         break;
392                 }
393
394                 if (!trace.ent)
395                 {
396                         Con_Printf ("SV_FlyMove: !trace.ent");
397                         trace.ent = prog->edicts;
398                 }
399
400                 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
401                         impact = false;
402                 else
403                 {
404                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
405                         impact = true;
406                 }
407
408                 if (trace.plane.normal[2])
409                 {
410                         if (trace.plane.normal[2] > 0.7)
411                         {
412                                 // floor
413                                 blocked |= 1;
414                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
415                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
416                         }
417                 }
418                 else
419                 {
420                         // step
421                         blocked |= 2;
422                         // save the trace for player extrafriction
423                         if (stepnormal)
424                                 VectorCopy(trace.plane.normal, stepnormal);
425                 }
426
427                 if (trace.fraction >= 0.001)
428                 {
429                         // actually covered some distance
430                         VectorCopy(trace.endpos, ent->fields.server->origin);
431                         VectorCopy(ent->fields.server->velocity, original_velocity);
432                         numplanes = 0;
433                 }
434
435                 // run the impact function
436                 if (impact)
437                 {
438                         SV_Impact(ent, &trace);
439
440                         // break if removed by the impact function
441                         if (ent->priv.server->free)
442                                 break;
443                 }
444
445                 time_left *= 1 - trace.fraction;
446
447                 // clipped to another plane
448                 if (numplanes >= MAX_CLIP_PLANES)
449                 {
450                         // this shouldn't really happen
451                         VectorClear(ent->fields.server->velocity);
452                         blocked = 3;
453                         break;
454                 }
455
456                 /*
457                 for (i = 0;i < numplanes;i++)
458                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
459                                 break;
460                 if (i < numplanes)
461                 {
462                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
463                         continue;
464                 }
465                 */
466
467                 VectorCopy(trace.plane.normal, planes[numplanes]);
468                 numplanes++;
469
470                 if (sv_newflymove.integer)
471                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
472                 else
473                 {
474                         // modify original_velocity so it parallels all of the clip planes
475                         for (i = 0;i < numplanes;i++)
476                         {
477                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
478                                 for (j = 0;j < numplanes;j++)
479                                 {
480                                         if (j != i)
481                                         {
482                                                 // not ok
483                                                 if (DotProduct(new_velocity, planes[j]) < 0)
484                                                         break;
485                                         }
486                                 }
487                                 if (j == numplanes)
488                                         break;
489                         }
490
491                         if (i != numplanes)
492                         {
493                                 // go along this plane
494                                 VectorCopy(new_velocity, ent->fields.server->velocity);
495                         }
496                         else
497                         {
498                                 // go along the crease
499                                 if (numplanes != 2)
500                                 {
501                                         VectorClear(ent->fields.server->velocity);
502                                         blocked = 7;
503                                         break;
504                                 }
505                                 CrossProduct(planes[0], planes[1], dir);
506                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
507                                 VectorNormalize(dir);
508                                 d = DotProduct(dir, ent->fields.server->velocity);
509                                 VectorScale(dir, d, ent->fields.server->velocity);
510                         }
511                 }
512
513                 // if current velocity is against the original velocity,
514                 // stop dead to avoid tiny occilations in sloping corners
515                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
516                 {
517                         VectorClear(ent->fields.server->velocity);
518                         break;
519                 }
520         }
521
522         //Con_Printf("entity %i final: blocked %i velocity %f %f %f\n", ent - prog->edicts, blocked, ent->fields.server->velocity[0], ent->fields.server->velocity[1], ent->fields.server->velocity[2]);
523
524         /*
525         if ((blocked & 1) == 0 && bumpcount > 1)
526         {
527                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
528                 // flag ONGROUND if there's ground under it
529                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
530         }
531         */
532
533         // LordHavoc: this came from QW and allows you to get out of water more easily
534         if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
535                 VectorCopy(primal_velocity, ent->fields.server->velocity);
536         return blocked;
537 }
538
539 /*
540 ============
541 SV_AddGravity
542
543 ============
544 */
545 void SV_AddGravity (prvm_edict_t *ent)
546 {
547         float ent_gravity;
548         prvm_eval_t *val;
549
550         val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
551         if (val!=0 && val->_float)
552                 ent_gravity = val->_float;
553         else
554                 ent_gravity = 1.0;
555         ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
556 }
557
558
559 /*
560 ===============================================================================
561
562 PUSHMOVE
563
564 ===============================================================================
565 */
566
567 /*
568 ============
569 SV_PushEntity
570
571 Does not change the entities velocity at all
572 ============
573 */
574 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonstartsolid)
575 {
576         int type;
577         trace_t trace;
578         vec3_t end;
579
580         VectorAdd (ent->fields.server->origin, push, end);
581
582         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
583                 type = MOVE_MISSILE;
584         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
585                 type = MOVE_NOMONSTERS; // only clip against bmodels
586         else
587                 type = MOVE_NORMAL;
588
589         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
590         if (trace.startsolid && failonstartsolid)
591                 return trace;
592
593         VectorCopy (trace.endpos, ent->fields.server->origin);
594         SV_LinkEdict (ent, true);
595
596         if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
597                 SV_Impact (ent, &trace);
598         return trace;
599 }
600
601
602 /*
603 ============
604 SV_PushMove
605
606 ============
607 */
608 void SV_PushMove (prvm_edict_t *pusher, float movetime)
609 {
610         int i, e, index;
611         float savesolid, movetime2, pushltime;
612         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
613         int num_moved;
614         int numcheckentities;
615         static prvm_edict_t *checkentities[MAX_EDICTS];
616         model_t *pushermodel;
617         trace_t trace;
618
619         if (!pusher->fields.server->velocity[0] && !pusher->fields.server->velocity[1] && !pusher->fields.server->velocity[2] && !pusher->fields.server->avelocity[0] && !pusher->fields.server->avelocity[1] && !pusher->fields.server->avelocity[2])
620         {
621                 pusher->fields.server->ltime += movetime;
622                 return;
623         }
624
625         switch ((int) pusher->fields.server->solid)
626         {
627         // LordHavoc: valid pusher types
628         case SOLID_BSP:
629         case SOLID_BBOX:
630         case SOLID_SLIDEBOX:
631         case SOLID_CORPSE: // LordHavoc: this would be weird...
632                 break;
633         // LordHavoc: no collisions
634         case SOLID_NOT:
635         case SOLID_TRIGGER:
636                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
637                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
638                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
639                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
640                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
641                 pusher->fields.server->ltime += movetime;
642                 SV_LinkEdict (pusher, false);
643                 return;
644         default:
645                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
646                 return;
647         }
648         index = (int) pusher->fields.server->modelindex;
649         if (index < 1 || index >= MAX_MODELS)
650         {
651                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
652                 return;
653         }
654         pushermodel = sv.models[index];
655
656         movetime2 = movetime;
657         VectorScale(pusher->fields.server->velocity, movetime2, move1);
658         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
659         if (moveangle[0] || moveangle[2])
660         {
661                 for (i = 0;i < 3;i++)
662                 {
663                         if (move1[i] > 0)
664                         {
665                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
666                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
667                         }
668                         else
669                         {
670                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
671                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
672                         }
673                 }
674         }
675         else if (moveangle[1])
676         {
677                 for (i = 0;i < 3;i++)
678                 {
679                         if (move1[i] > 0)
680                         {
681                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
682                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
683                         }
684                         else
685                         {
686                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
687                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
688                         }
689                 }
690         }
691         else
692         {
693                 for (i = 0;i < 3;i++)
694                 {
695                         if (move1[i] > 0)
696                         {
697                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
698                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
699                         }
700                         else
701                         {
702                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
703                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
704                         }
705                 }
706         }
707
708         VectorNegate (moveangle, a);
709         AngleVectorsFLU (a, forward, left, up);
710
711         VectorCopy (pusher->fields.server->origin, pushorig);
712         VectorCopy (pusher->fields.server->angles, pushang);
713         pushltime = pusher->fields.server->ltime;
714
715 // move the pusher to its final position
716
717         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
718         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
719         pusher->fields.server->ltime += movetime;
720         SV_LinkEdict (pusher, false);
721
722         savesolid = pusher->fields.server->solid;
723
724 // see if any solid entities are inside the final position
725         num_moved = 0;
726
727         numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
728         for (e = 0;e < numcheckentities;e++)
729         {
730                 prvm_edict_t *check = checkentities[e];
731                 if (check->fields.server->movetype == MOVETYPE_NONE
732                  || check->fields.server->movetype == MOVETYPE_PUSH
733                  || check->fields.server->movetype == MOVETYPE_FOLLOW
734                  || check->fields.server->movetype == MOVETYPE_NOCLIP
735                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
736                         continue;
737
738                 // if the entity is standing on the pusher, it will definitely be moved
739                 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
740                 {
741                         // remove the onground flag for non-players
742                         if (check->fields.server->movetype != MOVETYPE_WALK)
743                                 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
744                 }
745                 else
746                 {
747                         // if the entity is not inside the pusher's final position, leave it alone
748                         if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
749                                 continue;
750                 }
751
752
753                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
754                 {
755                         vec3_t org2;
756                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
757                         org2[0] = DotProduct (org, forward);
758                         org2[1] = DotProduct (org, left);
759                         org2[2] = DotProduct (org, up);
760                         VectorSubtract (org2, org, move);
761                         VectorAdd (move, move1, move);
762                 }
763                 else
764                         VectorCopy (move1, move);
765
766                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
767                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
768                 sv.moved_edicts[num_moved++] = check;
769
770                 // try moving the contacted entity
771                 pusher->fields.server->solid = SOLID_NOT;
772                 trace = SV_PushEntity (check, move, true);
773                 // FIXME: turn players specially
774                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
775                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
776                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
777
778                 // if it is still inside the pusher, block
779                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
780                 {
781                         // try moving the contacted entity a tiny bit further to account for precision errors
782                         vec3_t move2;
783                         pusher->fields.server->solid = SOLID_NOT;
784                         VectorScale(move, 1.1, move2);
785                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
786                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
787                         SV_PushEntity (check, move2, true);
788                         pusher->fields.server->solid = savesolid;
789                         if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
790                         {
791                                 // try moving the contacted entity a tiny bit less to account for precision errors
792                                 pusher->fields.server->solid = SOLID_NOT;
793                                 VectorScale(move, 0.9, move2);
794                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
795                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
796                                 SV_PushEntity (check, move2, true);
797                                 pusher->fields.server->solid = savesolid;
798                                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
799                                 {
800                                         // still inside pusher, so it's really blocked
801
802                                         // fail the move
803                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
804                                                 continue;
805                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
806                                         {
807                                                 // corpse
808                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
809                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
810                                                 continue;
811                                         }
812
813                                         VectorCopy (pushorig, pusher->fields.server->origin);
814                                         VectorCopy (pushang, pusher->fields.server->angles);
815                                         pusher->fields.server->ltime = pushltime;
816                                         SV_LinkEdict (pusher, false);
817
818                                         // move back any entities we already moved
819                                         for (i = 0;i < num_moved;i++)
820                                         {
821                                                 prvm_edict_t *ed = sv.moved_edicts[i];
822                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
823                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
824                                                 SV_LinkEdict (ed, false);
825                                         }
826
827                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
828                                         if (pusher->fields.server->blocked)
829                                         {
830                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
831                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
832                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
833                                         }
834                                         break;
835                                 }
836                         }
837                 }
838         }
839         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
840         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
841         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
842 }
843
844 /*
845 ================
846 SV_Physics_Pusher
847
848 ================
849 */
850 void SV_Physics_Pusher (prvm_edict_t *ent)
851 {
852         float thinktime, oldltime, movetime;
853
854         oldltime = ent->fields.server->ltime;
855
856         thinktime = ent->fields.server->nextthink;
857         if (thinktime < ent->fields.server->ltime + sv.frametime)
858         {
859                 movetime = thinktime - ent->fields.server->ltime;
860                 if (movetime < 0)
861                         movetime = 0;
862         }
863         else
864                 movetime = sv.frametime;
865
866         if (movetime)
867                 // advances ent->fields.server->ltime if not blocked
868                 SV_PushMove (ent, movetime);
869
870         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
871         {
872                 ent->fields.server->nextthink = 0;
873                 prog->globals.server->time = sv.time;
874                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
875                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
876                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
877         }
878 }
879
880
881 /*
882 ===============================================================================
883
884 CLIENT MOVEMENT
885
886 ===============================================================================
887 */
888
889 /*
890 =============
891 SV_CheckStuck
892
893 This is a big hack to try and fix the rare case of getting stuck in the world
894 clipping hull.
895 =============
896 */
897 void SV_CheckStuck (prvm_edict_t *ent)
898 {
899         int i, j, z;
900         vec3_t org;
901
902         if (!SV_TestEntityPosition(ent))
903         {
904                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
905                 return;
906         }
907
908         VectorCopy (ent->fields.server->origin, org);
909         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
910         if (!SV_TestEntityPosition(ent))
911         {
912                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
913                 SV_LinkEdict (ent, true);
914                 return;
915         }
916
917         for (z=-1 ; z< 18 ; z++)
918                 for (i=-1 ; i <= 1 ; i++)
919                         for (j=-1 ; j <= 1 ; j++)
920                         {
921                                 ent->fields.server->origin[0] = org[0] + i;
922                                 ent->fields.server->origin[1] = org[1] + j;
923                                 ent->fields.server->origin[2] = org[2] + z;
924                                 if (!SV_TestEntityPosition(ent))
925                                 {
926                                         Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
927                                         SV_LinkEdict (ent, true);
928                                         return;
929                                 }
930                         }
931
932         VectorCopy (org, ent->fields.server->origin);
933         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
934 }
935
936 static void SV_UnstickEntity (prvm_edict_t *ent)
937 {
938         int i, j, z;
939         vec3_t org;
940
941         // if not stuck in a bmodel, just return
942         if (!SV_TestEntityPosition(ent))
943                 return;
944
945         VectorCopy (ent->fields.server->origin, org);
946
947         for (z=-1 ; z< 18 ; z += 6)
948                 for (i=-1 ; i <= 1 ; i++)
949                         for (j=-1 ; j <= 1 ; j++)
950                         {
951                                 ent->fields.server->origin[0] = org[0] + i;
952                                 ent->fields.server->origin[1] = org[1] + j;
953                                 ent->fields.server->origin[2] = org[2] + z;
954                                 if (!SV_TestEntityPosition(ent))
955                                 {
956                                         Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), (float)i, (float)j, (float)z);
957                                         SV_LinkEdict (ent, true);
958                                         return;
959                                 }
960                         }
961
962         VectorCopy (org, ent->fields.server->origin);
963         Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
964 }
965
966
967 /*
968 =============
969 SV_CheckWater
970 =============
971 */
972 qboolean SV_CheckWater (prvm_edict_t *ent)
973 {
974         int cont;
975         vec3_t point;
976
977         point[0] = ent->fields.server->origin[0];
978         point[1] = ent->fields.server->origin[1];
979         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
980
981         ent->fields.server->waterlevel = 0;
982         ent->fields.server->watertype = CONTENTS_EMPTY;
983         cont = SV_PointSuperContents(point);
984         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
985         {
986                 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
987                 ent->fields.server->waterlevel = 1;
988                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
989                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
990                 {
991                         ent->fields.server->waterlevel = 2;
992                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
993                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
994                                 ent->fields.server->waterlevel = 3;
995                 }
996         }
997
998         return ent->fields.server->waterlevel > 1;
999 }
1000
1001 /*
1002 ============
1003 SV_WallFriction
1004
1005 ============
1006 */
1007 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1008 {
1009         float d, i;
1010         vec3_t forward, into, side;
1011
1012         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1013         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1014         {
1015                 // cut the tangential velocity
1016                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1017                 VectorScale (stepnormal, i, into);
1018                 VectorSubtract (ent->fields.server->velocity, into, side);
1019                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1020                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1021         }
1022 }
1023
1024 /*
1025 =====================
1026 SV_TryUnstick
1027
1028 Player has come to a dead stop, possibly due to the problem with limited
1029 float precision at some angle joins in the BSP hull.
1030
1031 Try fixing by pushing one pixel in each direction.
1032
1033 This is a hack, but in the interest of good gameplay...
1034 ======================
1035 */
1036 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1037 {
1038         int i, clip;
1039         vec3_t oldorg, dir;
1040
1041         VectorCopy (ent->fields.server->origin, oldorg);
1042         VectorClear (dir);
1043
1044         for (i=0 ; i<8 ; i++)
1045         {
1046                 // try pushing a little in an axial direction
1047                 switch (i)
1048                 {
1049                         case 0: dir[0] = 2; dir[1] = 0; break;
1050                         case 1: dir[0] = 0; dir[1] = 2; break;
1051                         case 2: dir[0] = -2; dir[1] = 0; break;
1052                         case 3: dir[0] = 0; dir[1] = -2; break;
1053                         case 4: dir[0] = 2; dir[1] = 2; break;
1054                         case 5: dir[0] = -2; dir[1] = 2; break;
1055                         case 6: dir[0] = 2; dir[1] = -2; break;
1056                         case 7: dir[0] = -2; dir[1] = -2; break;
1057                 }
1058
1059                 SV_PushEntity (ent, dir, false);
1060
1061                 // retry the original move
1062                 ent->fields.server->velocity[0] = oldvel[0];
1063                 ent->fields.server->velocity[1] = oldvel[1];
1064                 ent->fields.server->velocity[2] = 0;
1065                 clip = SV_FlyMove (ent, 0.1, NULL);
1066
1067                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1068                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1069                 {
1070                         Con_DPrint("TryUnstick - success.\n");
1071                         return clip;
1072                 }
1073
1074                 // go back to the original pos and try again
1075                 VectorCopy (oldorg, ent->fields.server->origin);
1076         }
1077
1078         // still not moving
1079         VectorClear (ent->fields.server->velocity);
1080         Con_DPrint("TryUnstick - failure.\n");
1081         return 7;
1082 }
1083
1084 /*
1085 =====================
1086 SV_WalkMove
1087
1088 Only used by players
1089 ======================
1090 */
1091 void SV_WalkMove (prvm_edict_t *ent)
1092 {
1093         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1094         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1095         trace_t downtrace;
1096
1097         SV_CheckVelocity(ent);
1098
1099         // do a regular slide move unless it looks like you ran into a step
1100         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1101         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1102
1103         VectorCopy (ent->fields.server->origin, start_origin);
1104         VectorCopy (ent->fields.server->velocity, start_velocity);
1105
1106         clip = SV_FlyMove (ent, sv.frametime, NULL);
1107
1108         SV_CheckVelocity(ent);
1109
1110         VectorCopy(ent->fields.server->origin, originalmove_origin);
1111         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1112         originalmove_clip = clip;
1113         originalmove_flags = (int)ent->fields.server->flags;
1114         originalmove_groundentity = ent->fields.server->groundentity;
1115
1116         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1117                 return;
1118
1119         if (sv_nostep.integer)
1120                 return;
1121
1122         // if move didn't block on a step, return
1123         if (clip & 2)
1124         {
1125                 // if move was not trying to move into the step, return
1126                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1127                         return;
1128
1129                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1130                 {
1131                         // return if gibbed by a trigger
1132                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1133                                 return;
1134
1135                         // only step up while jumping if that is enabled
1136                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1137                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1138                                         return;
1139                 }
1140
1141                 // try moving up and forward to go up a step
1142                 // back to start pos
1143                 VectorCopy (start_origin, ent->fields.server->origin);
1144                 VectorCopy (start_velocity, ent->fields.server->velocity);
1145
1146                 // move up
1147                 VectorClear (upmove);
1148                 upmove[2] = sv_stepheight.value;
1149                 // FIXME: don't link?
1150                 SV_PushEntity(ent, upmove, false);
1151
1152                 // move forward
1153                 ent->fields.server->velocity[2] = 0;
1154                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1155                 ent->fields.server->velocity[2] += start_velocity[2];
1156
1157                 SV_CheckVelocity(ent);
1158
1159                 // check for stuckness, possibly due to the limited precision of floats
1160                 // in the clipping hulls
1161                 if (clip
1162                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1163                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1164                 {
1165                         //Con_Printf("wall\n");
1166                         // stepping up didn't make any progress, revert to original move
1167                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1168                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1169                         //clip = originalmove_clip;
1170                         ent->fields.server->flags = originalmove_flags;
1171                         ent->fields.server->groundentity = originalmove_groundentity;
1172                         // now try to unstick if needed
1173                         //clip = SV_TryUnstick (ent, oldvel);
1174                         return;
1175                 }
1176
1177                 //Con_Printf("step - ");
1178
1179                 // extra friction based on view angle
1180                 if (clip & 2 && sv_wallfriction.integer)
1181                         SV_WallFriction (ent, stepnormal);
1182         }
1183         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1184         else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1185                 return;
1186
1187         // move down
1188         VectorClear (downmove);
1189         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1190         // FIXME: don't link?
1191         downtrace = SV_PushEntity (ent, downmove, false);
1192
1193         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1194         {
1195                 // this has been disabled so that you can't jump when you are stepping
1196                 // up while already jumping (also known as the Quake2 stair jump bug)
1197 #if 0
1198                 // LordHavoc: disabled this check so you can walk on monsters/players
1199                 //if (ent->fields.server->solid == SOLID_BSP)
1200                 {
1201                         //Con_Printf("onground\n");
1202                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1203                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1204                 }
1205 #endif
1206         }
1207         else
1208         {
1209                 //Con_Printf("slope\n");
1210                 // if the push down didn't end up on good ground, use the move without
1211                 // the step up.  This happens near wall / slope combinations, and can
1212                 // cause the player to hop up higher on a slope too steep to climb
1213                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1214                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1215                 //clip = originalmove_clip;
1216                 ent->fields.server->flags = originalmove_flags;
1217                 ent->fields.server->groundentity = originalmove_groundentity;
1218         }
1219
1220         SV_CheckVelocity(ent);
1221 }
1222
1223 //============================================================================
1224
1225 /*
1226 =============
1227 SV_Physics_Follow
1228
1229 Entities that are "stuck" to another entity
1230 =============
1231 */
1232 void SV_Physics_Follow (prvm_edict_t *ent)
1233 {
1234         vec3_t vf, vr, vu, angles, v;
1235         prvm_edict_t *e;
1236
1237         // regular thinking
1238         if (!SV_RunThink (ent))
1239                 return;
1240
1241         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1242         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1243         if (e->fields.server->angles[0] == ent->fields.server->punchangle[0] && e->fields.server->angles[1] == ent->fields.server->punchangle[1] && e->fields.server->angles[2] == ent->fields.server->punchangle[2])
1244         {
1245                 // quick case for no rotation
1246                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1247         }
1248         else
1249         {
1250                 angles[0] = -ent->fields.server->punchangle[0];
1251                 angles[1] =  ent->fields.server->punchangle[1];
1252                 angles[2] =  ent->fields.server->punchangle[2];
1253                 AngleVectors (angles, vf, vr, vu);
1254                 v[0] = ent->fields.server->view_ofs[0] * vf[0] + ent->fields.server->view_ofs[1] * vr[0] + ent->fields.server->view_ofs[2] * vu[0];
1255                 v[1] = ent->fields.server->view_ofs[0] * vf[1] + ent->fields.server->view_ofs[1] * vr[1] + ent->fields.server->view_ofs[2] * vu[1];
1256                 v[2] = ent->fields.server->view_ofs[0] * vf[2] + ent->fields.server->view_ofs[1] * vr[2] + ent->fields.server->view_ofs[2] * vu[2];
1257                 angles[0] = -e->fields.server->angles[0];
1258                 angles[1] =  e->fields.server->angles[1];
1259                 angles[2] =  e->fields.server->angles[2];
1260                 AngleVectors (angles, vf, vr, vu);
1261                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1262                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1263                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1264         }
1265         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1266         SV_LinkEdict (ent, true);
1267 }
1268
1269 /*
1270 ==============================================================================
1271
1272 TOSS / BOUNCE
1273
1274 ==============================================================================
1275 */
1276
1277 /*
1278 =============
1279 SV_CheckWaterTransition
1280
1281 =============
1282 */
1283 void SV_CheckWaterTransition (prvm_edict_t *ent)
1284 {
1285         int cont;
1286         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1287         if (!ent->fields.server->watertype)
1288         {
1289                 // just spawned here
1290                 ent->fields.server->watertype = cont;
1291                 ent->fields.server->waterlevel = 1;
1292                 return;
1293         }
1294
1295         // check if the entity crossed into or out of water
1296         if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1297                 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1298
1299         if (cont <= CONTENTS_WATER)
1300         {
1301                 ent->fields.server->watertype = cont;
1302                 ent->fields.server->waterlevel = 1;
1303         }
1304         else
1305         {
1306                 ent->fields.server->watertype = CONTENTS_EMPTY;
1307                 ent->fields.server->waterlevel = 0;
1308         }
1309 }
1310
1311 /*
1312 =============
1313 SV_Physics_Toss
1314
1315 Toss, bounce, and fly movement.  When onground, do nothing.
1316 =============
1317 */
1318 void SV_Physics_Toss (prvm_edict_t *ent)
1319 {
1320         trace_t trace;
1321         vec3_t move;
1322
1323 // if onground, return without moving
1324         if ((int)ent->fields.server->flags & FL_ONGROUND)
1325         {
1326                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1327                 {
1328                         // don't stick to ground if onground and moving upward
1329                         ent->fields.server->flags -= FL_ONGROUND;
1330                 }
1331                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1332                 {
1333                         // we can trust FL_ONGROUND if groundentity is world because it never moves
1334                         return;
1335                 }
1336                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1337                 {
1338                         // if ent was supported by a brush model on previous frame,
1339                         // and groundentity is now freed, set groundentity to 0 (world)
1340                         // which leaves it suspended in the air
1341                         ent->fields.server->groundentity = 0;
1342                         return;
1343                 }
1344         }
1345         ent->priv.server->suspendedinairflag = false;
1346
1347         SV_CheckVelocity (ent);
1348
1349 // add gravity
1350         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1351                 SV_AddGravity (ent);
1352
1353 // move angles
1354         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1355
1356 // move origin
1357         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1358         trace = SV_PushEntity (ent, move, true);
1359         if (ent->priv.server->free)
1360                 return;
1361         if (trace.bmodelstartsolid)
1362         {
1363                 // try to unstick the entity
1364                 SV_UnstickEntity(ent);
1365                 trace = SV_PushEntity (ent, move, false);
1366                 if (ent->priv.server->free)
1367                         return;
1368         }
1369
1370         if (trace.fraction < 1)
1371         {
1372                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1373                 {
1374                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1375                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1376                 }
1377                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1378                 {
1379                         float d;
1380                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1381                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1382                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1383                         {
1384                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1385                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1386                                 {
1387                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1388                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1389                                         VectorClear (ent->fields.server->velocity);
1390                                         VectorClear (ent->fields.server->avelocity);
1391                                 }
1392                                 else
1393                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1394                         }
1395                         else
1396                         {
1397                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1398                                 {
1399                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1400                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1401                                         VectorClear (ent->fields.server->velocity);
1402                                         VectorClear (ent->fields.server->avelocity);
1403                                 }
1404                                 else
1405                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1406                         }
1407                 }
1408                 else
1409                 {
1410                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1411                         if (trace.plane.normal[2] > 0.7)
1412                         {
1413                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1414                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1415                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1416                                         ent->priv.server->suspendedinairflag = true;
1417                                 VectorClear (ent->fields.server->velocity);
1418                                 VectorClear (ent->fields.server->avelocity);
1419                         }
1420                         else
1421                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1422                 }
1423         }
1424
1425 // check for in water
1426         SV_CheckWaterTransition (ent);
1427 }
1428
1429 /*
1430 ===============================================================================
1431
1432 STEPPING MOVEMENT
1433
1434 ===============================================================================
1435 */
1436
1437 /*
1438 =============
1439 SV_Physics_Step
1440
1441 Monsters freefall when they don't have a ground entity, otherwise
1442 all movement is done with discrete steps.
1443
1444 This is also used for objects that have become still on the ground, but
1445 will fall if the floor is pulled out from under them.
1446 =============
1447 */
1448 void SV_Physics_Step (prvm_edict_t *ent)
1449 {
1450         int flags = (int)ent->fields.server->flags;
1451         // don't fall at all if fly/swim
1452         if (!(flags & (FL_FLY | FL_SWIM)))
1453         {
1454                 if (flags & FL_ONGROUND)
1455                 {
1456                         // freefall if onground and moving upward
1457                         // freefall if not standing on a world surface (it may be a lift or trap door)
1458                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1459                         {
1460                                 ent->fields.server->flags -= FL_ONGROUND;
1461                                 SV_AddGravity(ent);
1462                                 SV_CheckVelocity(ent);
1463                                 SV_FlyMove(ent, sv.frametime, NULL);
1464                                 SV_LinkEdict(ent, true);
1465                         }
1466                 }
1467                 else
1468                 {
1469                         // freefall if not onground
1470                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1471
1472                         SV_AddGravity(ent);
1473                         SV_CheckVelocity(ent);
1474                         SV_FlyMove(ent, sv.frametime, NULL);
1475                         SV_LinkEdict(ent, true);
1476
1477                         // just hit ground
1478                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1479                                 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1480                 }
1481         }
1482
1483 // regular thinking
1484         SV_RunThink(ent);
1485
1486         SV_CheckWaterTransition(ent);
1487 }
1488
1489 //============================================================================
1490
1491 static void SV_Physics_Entity (prvm_edict_t *ent)
1492 {
1493         // don't run a move on newly spawned projectiles as it messes up movement
1494         // interpolation and rocket trails
1495         qboolean runmove = ent->priv.server->move;
1496         ent->priv.server->move = true;
1497         switch ((int) ent->fields.server->movetype)
1498         {
1499         case MOVETYPE_PUSH:
1500         case MOVETYPE_FAKEPUSH:
1501                 SV_Physics_Pusher (ent);
1502                 break;
1503         case MOVETYPE_NONE:
1504                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1505                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1506                         SV_RunThink (ent);
1507                 break;
1508         case MOVETYPE_FOLLOW:
1509                 SV_Physics_Follow (ent);
1510                 break;
1511         case MOVETYPE_NOCLIP:
1512                 if (SV_RunThink(ent))
1513                 {
1514                         SV_CheckWater(ent);
1515                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1516                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1517                 }
1518                 SV_LinkEdict(ent, false);
1519                 break;
1520         case MOVETYPE_STEP:
1521                 SV_Physics_Step (ent);
1522                 break;
1523         case MOVETYPE_WALK:
1524                 if (SV_RunThink (ent))
1525                 {
1526                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1527                                 SV_AddGravity (ent);
1528                         SV_CheckStuck (ent);
1529                         SV_WalkMove (ent);
1530                         SV_LinkEdict (ent, true);
1531                 }
1532                 break;
1533         case MOVETYPE_TOSS:
1534         case MOVETYPE_BOUNCE:
1535         case MOVETYPE_BOUNCEMISSILE:
1536         case MOVETYPE_FLYMISSILE:
1537         case MOVETYPE_FLY:
1538                 // regular thinking
1539                 if (SV_RunThink (ent) && runmove)
1540                         SV_Physics_Toss (ent);
1541                 break;
1542         default:
1543                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1544                 break;
1545         }
1546 }
1547
1548 void SV_ApplyClientMove (void);
1549 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1550 {
1551         SV_ApplyClientMove();
1552         // make sure the velocity is sane (not a NaN)
1553         SV_CheckVelocity(ent);
1554         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1555         if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1556         {
1557                 prog->globals.server->time = sv.time;
1558                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1559                 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1560         }
1561         else
1562                 SV_ClientThink ();
1563         // make sure the velocity is sane (not a NaN)
1564         SV_CheckVelocity(ent);
1565         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1566         // player_run/player_stand1 does not horribly malfunction if the
1567         // velocity becomes a number that is both == 0 and != 0
1568         // (sounds to me like NaN but to be absolutely safe...)
1569         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1570                 VectorClear(ent->fields.server->velocity);
1571         // call standard client pre-think
1572         prog->globals.server->time = sv.time;
1573         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1574         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1575         SV_CheckVelocity (ent);
1576
1577         switch ((int) ent->fields.server->movetype)
1578         {
1579         case MOVETYPE_PUSH:
1580         case MOVETYPE_FAKEPUSH:
1581                 SV_Physics_Pusher (ent);
1582                 break;
1583         case MOVETYPE_NONE:
1584                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1585                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1586                         SV_RunThink (ent);
1587                 break;
1588         case MOVETYPE_FOLLOW:
1589                 SV_Physics_Follow (ent);
1590                 break;
1591         case MOVETYPE_NOCLIP:
1592                 if (SV_RunThink(ent))
1593                 {
1594                         SV_CheckWater(ent);
1595                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1596                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1597                 }
1598                 break;
1599         case MOVETYPE_STEP:
1600                 SV_Physics_Step (ent);
1601                 break;
1602         case MOVETYPE_WALK:
1603                 if (SV_RunThink (ent))
1604                 {
1605                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1606                                 SV_AddGravity (ent);
1607                         SV_CheckStuck (ent);
1608                         SV_WalkMove (ent);
1609                 }
1610                 break;
1611         case MOVETYPE_TOSS:
1612         case MOVETYPE_BOUNCE:
1613         case MOVETYPE_BOUNCEMISSILE:
1614         case MOVETYPE_FLYMISSILE:
1615                 // regular thinking
1616                 if (SV_RunThink (ent))
1617                         SV_Physics_Toss (ent);
1618                 break;
1619         case MOVETYPE_FLY:
1620                 if (SV_RunThink (ent))
1621                 {
1622                         SV_CheckWater (ent);
1623                         SV_WalkMove (ent);
1624                 }
1625                 break;
1626         default:
1627                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1628                 break;
1629         }
1630
1631         SV_CheckVelocity (ent);
1632
1633         // call standard player post-think
1634         SV_LinkEdict (ent, true);
1635
1636         SV_CheckVelocity (ent);
1637
1638         prog->globals.server->time = sv.time;
1639         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1640         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1641 }
1642
1643 /*
1644 ================
1645 SV_Physics
1646
1647 ================
1648 */
1649 void SV_Physics (void)
1650 {
1651         int i;
1652         prvm_edict_t *ent;
1653
1654 // let the progs know that a new frame has started
1655         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1656         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1657         prog->globals.server->time = sv.time;
1658         prog->globals.server->frametime = sv.frametime;
1659         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1660
1661 //
1662 // treat each object in turn
1663 //
1664
1665         // if force_retouch, relink all the entities
1666         if (prog->globals.server->force_retouch > 0)
1667                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1668                         if (!ent->priv.server->free)
1669                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
1670
1671         // run physics on the client entities
1672         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1673         {
1674                 if (!ent->priv.server->free)
1675                 {
1676                         // don't do physics on disconnected clients, FrikBot relies on this
1677                         if (!host_client->spawned)
1678                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1679                         // don't run physics here if running asynchronously
1680                         else if (host_client->clmovement_skipphysicsframes > 0)
1681                                 host_client->clmovement_skipphysicsframes--;
1682                         else
1683                                 SV_Physics_ClientEntity(ent);
1684                 }
1685         }
1686
1687         // run physics on all the non-client entities
1688         if (!sv_freezenonclients.integer)
1689                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1690                         if (!ent->priv.server->free)
1691                                 SV_Physics_Entity(ent);
1692
1693         if (prog->globals.server->force_retouch > 0)
1694                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1695
1696         // LordHavoc: endframe support
1697         if (EndFrameQC)
1698         {
1699                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1700                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1701                 prog->globals.server->time = sv.time;
1702                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1703         }
1704
1705         // decrement prog->num_edicts if the highest number entities died
1706         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1707
1708         if (!sv_freezenonclients.integer)
1709                 sv.time += sv.frametime;
1710 }
1711
1712
1713 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1714 {
1715         int i;
1716         float gravity;
1717         vec3_t move, end;
1718         vec3_t original_origin;
1719         vec3_t original_velocity;
1720         vec3_t original_angles;
1721         vec3_t original_avelocity;
1722         prvm_eval_t *val;
1723         trace_t trace;
1724
1725         VectorCopy(tossent->fields.server->origin   , original_origin   );
1726         VectorCopy(tossent->fields.server->velocity , original_velocity );
1727         VectorCopy(tossent->fields.server->angles   , original_angles   );
1728         VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1729
1730         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1731         if (val != NULL && val->_float != 0)
1732                 gravity = val->_float;
1733         else
1734                 gravity = 1.0;
1735         gravity *= sv_gravity.value * 0.05;
1736
1737         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1738         {
1739                 SV_CheckVelocity (tossent);
1740                 tossent->fields.server->velocity[2] -= gravity;
1741                 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1742                 VectorScale (tossent->fields.server->velocity, 0.05, move);
1743                 VectorAdd (tossent->fields.server->origin, move, end);
1744                 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1745                 VectorCopy (trace.endpos, tossent->fields.server->origin);
1746
1747                 if (trace.fraction < 1)
1748                         break;
1749         }
1750
1751         VectorCopy(original_origin   , tossent->fields.server->origin   );
1752         VectorCopy(original_velocity , tossent->fields.server->velocity );
1753         VectorCopy(original_angles   , tossent->fields.server->angles   );
1754         VectorCopy(original_avelocity, tossent->fields.server->avelocity);
1755
1756         return trace;
1757 }
1758