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