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