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