eliminated qbyte type, now uses unsigned char throughout the engine for this purpose
[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"};
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, (prvm_edict_t *)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, (prvm_edict_t *)trace.ent);
516         return trace;
517 }
518
519
520 /*
521 ============
522 SV_PushMove
523
524 ============
525 */
526 void SV_PushMove (prvm_edict_t *pusher, float movetime)
527 {
528         int i, e, index;
529         float savesolid, movetime2, pushltime;
530         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
531         int num_moved;
532         int numcheckentities;
533         static prvm_edict_t *checkentities[MAX_EDICTS];
534         model_t *pushermodel;
535         trace_t trace;
536
537         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])
538         {
539                 pusher->fields.server->ltime += movetime;
540                 return;
541         }
542
543         switch ((int) pusher->fields.server->solid)
544         {
545         // LordHavoc: valid pusher types
546         case SOLID_BSP:
547         case SOLID_BBOX:
548         case SOLID_SLIDEBOX:
549         case SOLID_CORPSE: // LordHavoc: this would be weird...
550                 break;
551         // LordHavoc: no collisions
552         case SOLID_NOT:
553         case SOLID_TRIGGER:
554                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
555                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
556                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
557                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
558                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
559                 pusher->fields.server->ltime += movetime;
560                 SV_LinkEdict (pusher, false);
561                 return;
562         default:
563                 Con_Printf("SV_PushMove: unrecognized solid type %f\n", pusher->fields.server->solid);
564                 return;
565         }
566         index = (int) pusher->fields.server->modelindex;
567         if (index < 1 || index >= MAX_MODELS)
568         {
569                 Con_Printf("SV_PushMove: invalid modelindex %f\n", pusher->fields.server->modelindex);
570                 return;
571         }
572         pushermodel = sv.models[index];
573
574         movetime2 = movetime;
575         VectorScale(pusher->fields.server->velocity, movetime2, move1);
576         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
577         if (moveangle[0] || moveangle[2])
578         {
579                 for (i = 0;i < 3;i++)
580                 {
581                         if (move1[i] > 0)
582                         {
583                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
584                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
585                         }
586                         else
587                         {
588                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
589                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
590                         }
591                 }
592         }
593         else if (moveangle[1])
594         {
595                 for (i = 0;i < 3;i++)
596                 {
597                         if (move1[i] > 0)
598                         {
599                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
600                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
601                         }
602                         else
603                         {
604                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
605                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
606                         }
607                 }
608         }
609         else
610         {
611                 for (i = 0;i < 3;i++)
612                 {
613                         if (move1[i] > 0)
614                         {
615                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
616                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
617                         }
618                         else
619                         {
620                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
621                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
622                         }
623                 }
624         }
625
626         VectorNegate (moveangle, a);
627         AngleVectorsFLU (a, forward, left, up);
628
629         VectorCopy (pusher->fields.server->origin, pushorig);
630         VectorCopy (pusher->fields.server->angles, pushang);
631         pushltime = pusher->fields.server->ltime;
632
633 // move the pusher to its final position
634
635         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
636         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
637         pusher->fields.server->ltime += movetime;
638         SV_LinkEdict (pusher, false);
639
640         savesolid = pusher->fields.server->solid;
641
642 // see if any solid entities are inside the final position
643         num_moved = 0;
644
645         numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
646         for (e = 0;e < numcheckentities;e++)
647         {
648                 prvm_edict_t *check = checkentities[e];
649                 if (check->fields.server->movetype == MOVETYPE_NONE
650                  || check->fields.server->movetype == MOVETYPE_PUSH
651                  || check->fields.server->movetype == MOVETYPE_FOLLOW
652                  || check->fields.server->movetype == MOVETYPE_NOCLIP
653                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
654                         continue;
655
656                 // if the entity is standing on the pusher, it will definitely be moved
657                 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
658                 {
659                         // if the entity is not inside the pusher's final position, leave it alone
660                         if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
661                                 continue;
662                         // remove the onground flag for non-players
663                         if (check->fields.server->movetype != MOVETYPE_WALK)
664                                 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
665                 }
666
667
668                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
669                 {
670                         vec3_t org2;
671                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
672                         org2[0] = DotProduct (org, forward);
673                         org2[1] = DotProduct (org, left);
674                         org2[2] = DotProduct (org, up);
675                         VectorSubtract (org2, org, move);
676                         VectorAdd (move, move1, move);
677                 }
678                 else
679                         VectorCopy (move1, move);
680
681                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
682                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
683                 sv.moved_edicts[num_moved++] = check;
684
685                 // try moving the contacted entity
686                 pusher->fields.server->solid = SOLID_NOT;
687                 trace = SV_PushEntity (check, move);
688                 // FIXME: turn players specially
689                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
690                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
691
692                 // if it is still inside the pusher, block
693                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
694                 {
695                         // try moving the contacted entity a tiny bit further to account for precision errors
696                         vec3_t move2;
697                         pusher->fields.server->solid = SOLID_NOT;
698                         VectorScale(move, 1.1, move2);
699                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
700                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
701                         SV_PushEntity (check, move2);
702                         pusher->fields.server->solid = savesolid;
703                         if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
704                         {
705                                 // try moving the contacted entity a tiny bit less to account for precision errors
706                                 pusher->fields.server->solid = SOLID_NOT;
707                                 VectorScale(move, 0.9, move2);
708                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
709                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
710                                 SV_PushEntity (check, move2);
711                                 pusher->fields.server->solid = savesolid;
712                                 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID).startsolid)
713                                 {
714                                         // still inside pusher, so it's really blocked
715
716                                         // fail the move
717                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
718                                                 continue;
719                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
720                                         {
721                                                 // corpse
722                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
723                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
724                                                 continue;
725                                         }
726
727                                         VectorCopy (pushorig, pusher->fields.server->origin);
728                                         VectorCopy (pushang, pusher->fields.server->angles);
729                                         pusher->fields.server->ltime = pushltime;
730                                         SV_LinkEdict (pusher, false);
731
732                                         // move back any entities we already moved
733                                         for (i = 0;i < num_moved;i++)
734                                         {
735                                                 prvm_edict_t *ed = sv.moved_edicts[i];
736                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
737                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
738                                                 SV_LinkEdict (ed, false);
739                                         }
740
741                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
742                                         if (pusher->fields.server->blocked)
743                                         {
744                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
745                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
746                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
747                                         }
748                                         break;
749                                 }
750                         }
751                 }
752         }
753         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
754         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
755         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
756 }
757
758 /*
759 ================
760 SV_Physics_Pusher
761
762 ================
763 */
764 void SV_Physics_Pusher (prvm_edict_t *ent)
765 {
766         float thinktime, oldltime, movetime;
767
768         oldltime = ent->fields.server->ltime;
769
770         thinktime = ent->fields.server->nextthink;
771         if (thinktime < ent->fields.server->ltime + sv.frametime)
772         {
773                 movetime = thinktime - ent->fields.server->ltime;
774                 if (movetime < 0)
775                         movetime = 0;
776         }
777         else
778                 movetime = sv.frametime;
779
780         if (movetime)
781                 // advances ent->fields.server->ltime if not blocked
782                 SV_PushMove (ent, movetime);
783
784         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
785         {
786                 ent->fields.server->nextthink = 0;
787                 prog->globals.server->time = sv.time;
788                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
789                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
790                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
791         }
792 }
793
794
795 /*
796 ===============================================================================
797
798 CLIENT MOVEMENT
799
800 ===============================================================================
801 */
802
803 /*
804 =============
805 SV_CheckStuck
806
807 This is a big hack to try and fix the rare case of getting stuck in the world
808 clipping hull.
809 =============
810 */
811 void SV_CheckStuck (prvm_edict_t *ent)
812 {
813         int i, j, z;
814         vec3_t org;
815
816         if (!SV_TestEntityPosition(ent))
817         {
818                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
819                 return;
820         }
821
822         VectorCopy (ent->fields.server->origin, org);
823         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
824         if (!SV_TestEntityPosition(ent))
825         {
826                 Con_DPrint("Unstuck.\n");
827                 SV_LinkEdict (ent, true);
828                 return;
829         }
830
831         for (z=0 ; z< 18 ; z++)
832                 for (i=-1 ; i <= 1 ; i++)
833                         for (j=-1 ; j <= 1 ; j++)
834                         {
835                                 ent->fields.server->origin[0] = org[0] + i;
836                                 ent->fields.server->origin[1] = org[1] + j;
837                                 ent->fields.server->origin[2] = org[2] + z;
838                                 if (!SV_TestEntityPosition(ent))
839                                 {
840                                         Con_DPrint("Unstuck.\n");
841                                         SV_LinkEdict (ent, true);
842                                         return;
843                                 }
844                         }
845
846         VectorCopy (org, ent->fields.server->origin);
847         Con_DPrint("player is stuck.\n");
848 }
849
850
851 /*
852 =============
853 SV_CheckWater
854 =============
855 */
856 qboolean SV_CheckWater (prvm_edict_t *ent)
857 {
858         int cont;
859         vec3_t point;
860
861         point[0] = ent->fields.server->origin[0];
862         point[1] = ent->fields.server->origin[1];
863         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
864
865         ent->fields.server->waterlevel = 0;
866         ent->fields.server->watertype = CONTENTS_EMPTY;
867         cont = SV_PointSuperContents(point);
868         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
869         {
870                 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
871                 ent->fields.server->waterlevel = 1;
872                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
873                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
874                 {
875                         ent->fields.server->waterlevel = 2;
876                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
877                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
878                                 ent->fields.server->waterlevel = 3;
879                 }
880         }
881
882         return ent->fields.server->waterlevel > 1;
883 }
884
885 /*
886 ============
887 SV_WallFriction
888
889 ============
890 */
891 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
892 {
893         float d, i;
894         vec3_t forward, into, side;
895
896         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
897         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
898         {
899                 // cut the tangential velocity
900                 i = DotProduct (stepnormal, ent->fields.server->velocity);
901                 VectorScale (stepnormal, i, into);
902                 VectorSubtract (ent->fields.server->velocity, into, side);
903                 ent->fields.server->velocity[0] = side[0] * (1 + d);
904                 ent->fields.server->velocity[1] = side[1] * (1 + d);
905         }
906 }
907
908 /*
909 =====================
910 SV_TryUnstick
911
912 Player has come to a dead stop, possibly due to the problem with limited
913 float precision at some angle joins in the BSP hull.
914
915 Try fixing by pushing one pixel in each direction.
916
917 This is a hack, but in the interest of good gameplay...
918 ======================
919 */
920 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
921 {
922         int i, clip;
923         vec3_t oldorg, dir;
924
925         VectorCopy (ent->fields.server->origin, oldorg);
926         VectorClear (dir);
927
928         for (i=0 ; i<8 ; i++)
929         {
930                 // try pushing a little in an axial direction
931                 switch (i)
932                 {
933                         case 0: dir[0] = 2; dir[1] = 0; break;
934                         case 1: dir[0] = 0; dir[1] = 2; break;
935                         case 2: dir[0] = -2; dir[1] = 0; break;
936                         case 3: dir[0] = 0; dir[1] = -2; break;
937                         case 4: dir[0] = 2; dir[1] = 2; break;
938                         case 5: dir[0] = -2; dir[1] = 2; break;
939                         case 6: dir[0] = 2; dir[1] = -2; break;
940                         case 7: dir[0] = -2; dir[1] = -2; break;
941                 }
942
943                 SV_PushEntity (ent, dir);
944
945                 // retry the original move
946                 ent->fields.server->velocity[0] = oldvel[0];
947                 ent->fields.server->velocity[1] = oldvel[1];
948                 ent->fields.server->velocity[2] = 0;
949                 clip = SV_FlyMove (ent, 0.1, NULL);
950
951                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
952                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
953                 {
954                         Con_DPrint("TryUnstick - success.\n");
955                         return clip;
956                 }
957
958                 // go back to the original pos and try again
959                 VectorCopy (oldorg, ent->fields.server->origin);
960         }
961
962         // still not moving
963         VectorClear (ent->fields.server->velocity);
964         Con_DPrint("TryUnstick - failure.\n");
965         return 7;
966 }
967
968 /*
969 =====================
970 SV_WalkMove
971
972 Only used by players
973 ======================
974 */
975 void SV_WalkMove (prvm_edict_t *ent)
976 {
977         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
978         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
979         trace_t downtrace;
980
981         SV_CheckVelocity(ent);
982
983         // do a regular slide move unless it looks like you ran into a step
984         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
985         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
986
987         VectorCopy (ent->fields.server->origin, start_origin);
988         VectorCopy (ent->fields.server->velocity, start_velocity);
989
990         clip = SV_FlyMove (ent, sv.frametime, NULL);
991
992         SV_CheckVelocity(ent);
993
994         VectorCopy(ent->fields.server->origin, originalmove_origin);
995         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
996         originalmove_clip = clip;
997         originalmove_flags = (int)ent->fields.server->flags;
998         originalmove_groundentity = ent->fields.server->groundentity;
999
1000         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1001                 return;
1002
1003         if (sv_nostep.integer)
1004                 return;
1005
1006         // if move didn't block on a step, return
1007         if (clip & 2)
1008         {
1009                 // if move was not trying to move into the step, return
1010                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1011                         return;
1012
1013                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1014                 {
1015                         // return if gibbed by a trigger
1016                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1017                                 return;
1018
1019                         // only step up while jumping if that is enabled
1020                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1021                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1022                                         return;
1023                 }
1024
1025                 // try moving up and forward to go up a step
1026                 // back to start pos
1027                 VectorCopy (start_origin, ent->fields.server->origin);
1028                 VectorCopy (start_velocity, ent->fields.server->velocity);
1029
1030                 // move up
1031                 VectorClear (upmove);
1032                 upmove[2] = sv_stepheight.value;
1033                 // FIXME: don't link?
1034                 SV_PushEntity(ent, upmove);
1035
1036                 // move forward
1037                 ent->fields.server->velocity[2] = 0;
1038                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1039                 ent->fields.server->velocity[2] += start_velocity[2];
1040
1041                 SV_CheckVelocity(ent);
1042
1043                 // check for stuckness, possibly due to the limited precision of floats
1044                 // in the clipping hulls
1045                 if (clip
1046                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1047                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1048                 {
1049                         //Con_Printf("wall\n");
1050                         // stepping up didn't make any progress, revert to original move
1051                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1052                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1053                         //clip = originalmove_clip;
1054                         ent->fields.server->flags = originalmove_flags;
1055                         ent->fields.server->groundentity = originalmove_groundentity;
1056                         // now try to unstick if needed
1057                         //clip = SV_TryUnstick (ent, oldvel);
1058                         return;
1059                 }
1060
1061                 //Con_Printf("step - ");
1062
1063                 // extra friction based on view angle
1064                 if (clip & 2 && sv_wallfriction.integer)
1065                         SV_WallFriction (ent, stepnormal);
1066         }
1067         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1068         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)))
1069                 return;
1070
1071         // move down
1072         VectorClear (downmove);
1073         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1074         // FIXME: don't link?
1075         downtrace = SV_PushEntity (ent, downmove);
1076
1077         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1078         {
1079                 // LordHavoc: disabled this check so you can walk on monsters/players
1080                 //if (ent->fields.server->solid == SOLID_BSP)
1081                 {
1082                         //Con_Printf("onground\n");
1083                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1084                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1085                 }
1086         }
1087         else
1088         {
1089                 //Con_Printf("slope\n");
1090                 // if the push down didn't end up on good ground, use the move without
1091                 // the step up.  This happens near wall / slope combinations, and can
1092                 // cause the player to hop up higher on a slope too steep to climb
1093                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1094                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1095                 //clip = originalmove_clip;
1096                 ent->fields.server->flags = originalmove_flags;
1097                 ent->fields.server->groundentity = originalmove_groundentity;
1098         }
1099
1100         SV_CheckVelocity(ent);
1101 }
1102
1103 //============================================================================
1104
1105 /*
1106 =============
1107 SV_Physics_Follow
1108
1109 Entities that are "stuck" to another entity
1110 =============
1111 */
1112 void SV_Physics_Follow (prvm_edict_t *ent)
1113 {
1114         vec3_t vf, vr, vu, angles, v;
1115         prvm_edict_t *e;
1116
1117         // regular thinking
1118         if (!SV_RunThink (ent))
1119                 return;
1120
1121         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1122         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1123         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])
1124         {
1125                 // quick case for no rotation
1126                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1127         }
1128         else
1129         {
1130                 angles[0] = -ent->fields.server->punchangle[0];
1131                 angles[1] =  ent->fields.server->punchangle[1];
1132                 angles[2] =  ent->fields.server->punchangle[2];
1133                 AngleVectors (angles, vf, vr, vu);
1134                 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];
1135                 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];
1136                 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];
1137                 angles[0] = -e->fields.server->angles[0];
1138                 angles[1] =  e->fields.server->angles[1];
1139                 angles[2] =  e->fields.server->angles[2];
1140                 AngleVectors (angles, vf, vr, vu);
1141                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1142                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1143                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1144         }
1145         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1146         SV_LinkEdict (ent, true);
1147 }
1148
1149 /*
1150 ==============================================================================
1151
1152 TOSS / BOUNCE
1153
1154 ==============================================================================
1155 */
1156
1157 /*
1158 =============
1159 SV_CheckWaterTransition
1160
1161 =============
1162 */
1163 void SV_CheckWaterTransition (prvm_edict_t *ent)
1164 {
1165         int cont;
1166         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1167         if (!ent->fields.server->watertype)
1168         {
1169                 // just spawned here
1170                 ent->fields.server->watertype = cont;
1171                 ent->fields.server->waterlevel = 1;
1172                 return;
1173         }
1174
1175         // check if the entity crossed into or out of water
1176         if (gamemode != GAME_NEXUIZ && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1177                 SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
1178
1179         if (cont <= CONTENTS_WATER)
1180         {
1181                 ent->fields.server->watertype = cont;
1182                 ent->fields.server->waterlevel = 1;
1183         }
1184         else
1185         {
1186                 ent->fields.server->watertype = CONTENTS_EMPTY;
1187                 ent->fields.server->waterlevel = 0;
1188         }
1189 }
1190
1191 /*
1192 =============
1193 SV_Physics_Toss
1194
1195 Toss, bounce, and fly movement.  When onground, do nothing.
1196 =============
1197 */
1198 void SV_Physics_Toss (prvm_edict_t *ent)
1199 {
1200         trace_t trace;
1201         vec3_t move;
1202
1203         // don't stick to ground if onground and moving upward
1204         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1205                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1206
1207 // if onground, return without moving
1208         if ((int)ent->fields.server->flags & FL_ONGROUND)
1209         {
1210                 if (ent->fields.server->groundentity == 0 || sv_gameplayfix_noairborncorpse.integer)
1211                         return;
1212                 // if ent was supported by a brush model on previous frame,
1213                 // and groundentity is now freed, set groundentity to 0 (floating)
1214                 if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1215                 {
1216                         // leave it suspended in the air
1217                         ent->fields.server->groundentity = 0;
1218                         return;
1219                 }
1220         }
1221         ent->priv.server->suspendedinairflag = false;
1222
1223         SV_CheckVelocity (ent);
1224
1225 // add gravity
1226         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1227                 SV_AddGravity (ent);
1228
1229 // move angles
1230         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1231
1232 // move origin
1233         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1234         trace = SV_PushEntity (ent, move);
1235         if (ent->priv.server->free)
1236                 return;
1237
1238         if (trace.fraction < 1)
1239         {
1240                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1241                 {
1242                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1243                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1244                 }
1245                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1246                 {
1247                         float d;
1248                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1249                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1250                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1251                         {
1252                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1253                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1254                                 {
1255                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1256                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1257                                         VectorClear (ent->fields.server->velocity);
1258                                         VectorClear (ent->fields.server->avelocity);
1259                                 }
1260                                 else
1261                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1262                         }
1263                         else
1264                         {
1265                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1266                                 {
1267                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1268                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1269                                         VectorClear (ent->fields.server->velocity);
1270                                         VectorClear (ent->fields.server->avelocity);
1271                                 }
1272                                 else
1273                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1274                         }
1275                 }
1276                 else
1277                 {
1278                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1279                         if (trace.plane.normal[2] > 0.7)
1280                         {
1281                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1282                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1283                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1284                                         ent->priv.server->suspendedinairflag = true;
1285                                 VectorClear (ent->fields.server->velocity);
1286                                 VectorClear (ent->fields.server->avelocity);
1287                         }
1288                         else
1289                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1290                 }
1291         }
1292
1293 // check for in water
1294         SV_CheckWaterTransition (ent);
1295 }
1296
1297 /*
1298 ===============================================================================
1299
1300 STEPPING MOVEMENT
1301
1302 ===============================================================================
1303 */
1304
1305 /*
1306 =============
1307 SV_Physics_Step
1308
1309 Monsters freefall when they don't have a ground entity, otherwise
1310 all movement is done with discrete steps.
1311
1312 This is also used for objects that have become still on the ground, but
1313 will fall if the floor is pulled out from under them.
1314 =============
1315 */
1316 void SV_Physics_Step (prvm_edict_t *ent)
1317 {
1318         // don't stick to ground if onground and moving upward
1319         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && ((int)ent->fields.server->flags & FL_ONGROUND))
1320                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1321
1322         // freefall if not onground/fly/swim
1323         if (!((int)ent->fields.server->flags & (FL_ONGROUND | FL_FLY | FL_SWIM)))
1324         {
1325                 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1326
1327                 SV_AddGravity(ent);
1328                 SV_CheckVelocity(ent);
1329                 SV_FlyMove(ent, sv.frametime, NULL);
1330                 SV_LinkEdict(ent, true);
1331
1332                 // just hit ground
1333                 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1334                         SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1335         }
1336
1337 // regular thinking
1338         SV_RunThink(ent);
1339
1340         SV_CheckWaterTransition(ent);
1341 }
1342
1343 //============================================================================
1344
1345 static void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1346 {
1347         switch ((int) ent->fields.server->movetype)
1348         {
1349         case MOVETYPE_PUSH:
1350         case MOVETYPE_FAKEPUSH:
1351                 SV_Physics_Pusher (ent);
1352                 break;
1353         case MOVETYPE_NONE:
1354                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1355                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1356                         SV_RunThink (ent);
1357                 break;
1358         case MOVETYPE_FOLLOW:
1359                 SV_Physics_Follow (ent);
1360                 break;
1361         case MOVETYPE_NOCLIP:
1362                 if (SV_RunThink(ent))
1363                 {
1364                         SV_CheckWater(ent);
1365                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1366                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1367                 }
1368                 SV_LinkEdict(ent, false);
1369                 break;
1370         case MOVETYPE_STEP:
1371                 SV_Physics_Step (ent);
1372                 break;
1373         case MOVETYPE_WALK:
1374                 if (SV_RunThink (ent))
1375                 {
1376                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1377                                 SV_AddGravity (ent);
1378                         SV_CheckStuck (ent);
1379                         SV_WalkMove (ent);
1380                         SV_LinkEdict (ent, true);
1381                 }
1382                 break;
1383         case MOVETYPE_TOSS:
1384         case MOVETYPE_BOUNCE:
1385         case MOVETYPE_BOUNCEMISSILE:
1386         case MOVETYPE_FLYMISSILE:
1387         case MOVETYPE_FLY:
1388                 // regular thinking
1389                 if (SV_RunThink (ent) && runmove)
1390                         SV_Physics_Toss (ent);
1391                 break;
1392         default:
1393                 Con_Printf ("SV_Physics: bad movetype %i", (int)ent->fields.server->movetype);
1394                 break;
1395         }
1396 }
1397
1398 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1399 {
1400         // make sure the velocity is sane (not a NaN)
1401         SV_CheckVelocity(ent);
1402         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1403         if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1404         {
1405                 prog->globals.server->time = sv.time;
1406                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1407                 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1408         }
1409         else
1410                 SV_ClientThink ();
1411         // make sure the velocity is sane (not a NaN)
1412         SV_CheckVelocity(ent);
1413         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1414         // player_run/player_stand1 does not horribly malfunction if the
1415         // velocity becomes a number that is both == 0 and != 0
1416         // (sounds to me like NaN but to be absolutely safe...)
1417         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1418                 VectorClear(ent->fields.server->velocity);
1419         // call standard client pre-think
1420         prog->globals.server->time = sv.time;
1421         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1422         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1423         SV_CheckVelocity (ent);
1424
1425         switch ((int) ent->fields.server->movetype)
1426         {
1427         case MOVETYPE_PUSH:
1428         case MOVETYPE_FAKEPUSH:
1429                 SV_Physics_Pusher (ent);
1430                 break;
1431         case MOVETYPE_NONE:
1432                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1433                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1434                         SV_RunThink (ent);
1435                 break;
1436         case MOVETYPE_FOLLOW:
1437                 SV_Physics_Follow (ent);
1438                 break;
1439         case MOVETYPE_NOCLIP:
1440                 if (SV_RunThink(ent))
1441                 {
1442                         SV_CheckWater(ent);
1443                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1444                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1445                 }
1446                 break;
1447         case MOVETYPE_STEP:
1448                 SV_Physics_Step (ent);
1449                 break;
1450         case MOVETYPE_WALK:
1451                 if (SV_RunThink (ent))
1452                 {
1453                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1454                                 SV_AddGravity (ent);
1455                         SV_CheckStuck (ent);
1456                         SV_WalkMove (ent);
1457                 }
1458                 break;
1459         case MOVETYPE_TOSS:
1460         case MOVETYPE_BOUNCE:
1461         case MOVETYPE_BOUNCEMISSILE:
1462         case MOVETYPE_FLYMISSILE:
1463                 // regular thinking
1464                 if (SV_RunThink (ent))
1465                         SV_Physics_Toss (ent);
1466                 break;
1467         case MOVETYPE_FLY:
1468                 if (SV_RunThink (ent))
1469                 {
1470                         SV_CheckWater (ent);
1471                         SV_WalkMove (ent);
1472                 }
1473                 break;
1474         default:
1475                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i", (int)ent->fields.server->movetype);
1476                 break;
1477         }
1478
1479         SV_CheckVelocity (ent);
1480
1481         // call standard player post-think
1482         SV_LinkEdict (ent, true);
1483
1484         SV_CheckVelocity (ent);
1485
1486         prog->globals.server->time = sv.time;
1487         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1488         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1489 }
1490
1491 /*
1492 ================
1493 SV_Physics
1494
1495 ================
1496 */
1497 void SV_Physics (void)
1498 {
1499         int i, newnum_edicts;
1500         prvm_edict_t *ent;
1501         unsigned char runmove[MAX_EDICTS];
1502
1503 // let the progs know that a new frame has started
1504         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1505         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1506         prog->globals.server->time = sv.time;
1507         prog->globals.server->frametime = sv.frametime;
1508         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1509
1510         // don't run a move on newly spawned projectiles as it messes up movement
1511         // interpolation and rocket trails
1512         newnum_edicts = 0;
1513         for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1514                 if ((runmove[i] = !ent->priv.server->free))
1515                         newnum_edicts = i + 1;
1516         prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1517
1518 //
1519 // treat each object in turn
1520 //
1521
1522         // if force_retouch, relink all the entities
1523         if (prog->globals.server->force_retouch > 0)
1524                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1525                         if (!ent->priv.server->free)
1526                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
1527
1528         // run physics on the client entities
1529         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1530         {
1531                 if (!ent->priv.server->free)
1532                 {
1533                         // don't do physics on disconnected clients, FrikBot relies on this
1534                         if (!host_client->spawned)
1535                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1536                         // don't run physics here if running asynchronously
1537                         else if (!host_client->movesequence)
1538                                 SV_Physics_ClientEntity(ent);
1539                 }
1540         }
1541
1542         // run physics on all the non-client entities
1543         if (!sv_freezenonclients.integer)
1544                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1545                         if (!ent->priv.server->free)
1546                                 SV_Physics_Entity(ent, runmove[i]);
1547
1548         if (prog->globals.server->force_retouch > 0)
1549                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1550
1551         // LordHavoc: endframe support
1552         if (EndFrameQC)
1553         {
1554                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1555                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1556                 prog->globals.server->time = sv.time;
1557                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1558         }
1559
1560         if (!sv_freezenonclients.integer)
1561                 sv.time += sv.frametime;
1562 }
1563
1564
1565 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1566 {
1567         int i;
1568         float gravity, savesolid;
1569         vec3_t move, end;
1570         prvm_edict_t tempent, *tent;
1571         entvars_t vars;
1572         prvm_eval_t *val;
1573         trace_t trace;
1574
1575         // copy the vars over
1576         memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1577         // set up the temp entity to point to the copied vars
1578         tent = &tempent;
1579         tent->fields.server = &vars;
1580
1581         savesolid = tossent->fields.server->solid;
1582         tossent->fields.server->solid = SOLID_NOT;
1583
1584         // this has to fetch the field from the original edict, since our copy is truncated
1585         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1586         if (val != NULL && val->_float != 0)
1587                 gravity = val->_float;
1588         else
1589                 gravity = 1.0;
1590         gravity *= sv_gravity.value * 0.05;
1591
1592         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1593         {
1594                 SV_CheckVelocity (tent);
1595                 tent->fields.server->velocity[2] -= gravity;
1596                 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1597                 VectorScale (tent->fields.server->velocity, 0.05, move);
1598                 VectorAdd (tent->fields.server->origin, move, end);
1599                 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1600                 VectorCopy (trace.endpos, tent->fields.server->origin);
1601
1602                 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1603                         break;
1604         }
1605         tossent->fields.server->solid = savesolid;
1606         trace.fraction = 0; // not relevant
1607         return trace;
1608 }
1609