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