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