]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_phys.c
added DP_SV_CUSTOMIZEENTITYFORCLIENT extension based on a patch from [515]
[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, (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 // if onground, return without moving
1204         if ((int)ent->fields.server->flags & FL_ONGROUND)
1205         {
1206                 // don't stick to ground if onground and moving upward
1207                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1208                         ent->fields.server->flags -= FL_ONGROUND;
1209                 else
1210                 {
1211                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1212                         if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1213                                 return;
1214                         // if ent was supported by a brush model on previous frame,
1215                         // and groundentity is now freed, set groundentity to 0 (floating)
1216                         if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1217                         {
1218                                 // leave it suspended in the air
1219                                 ent->fields.server->groundentity = 0;
1220                                 return;
1221                         }
1222                 }
1223         }
1224         ent->priv.server->suspendedinairflag = false;
1225
1226         SV_CheckVelocity (ent);
1227
1228 // add gravity
1229         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1230                 SV_AddGravity (ent);
1231
1232 // move angles
1233         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1234
1235 // move origin
1236         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1237         trace = SV_PushEntity (ent, move);
1238         if (ent->priv.server->free)
1239                 return;
1240
1241         if (trace.fraction < 1)
1242         {
1243                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1244                 {
1245                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1246                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1247                 }
1248                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1249                 {
1250                         float d;
1251                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1252                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1253                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1254                         {
1255                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1256                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1257                                 {
1258                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1259                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1260                                         VectorClear (ent->fields.server->velocity);
1261                                         VectorClear (ent->fields.server->avelocity);
1262                                 }
1263                                 else
1264                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1265                         }
1266                         else
1267                         {
1268                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1269                                 {
1270                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1271                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1272                                         VectorClear (ent->fields.server->velocity);
1273                                         VectorClear (ent->fields.server->avelocity);
1274                                 }
1275                                 else
1276                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1277                         }
1278                 }
1279                 else
1280                 {
1281                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1282                         if (trace.plane.normal[2] > 0.7)
1283                         {
1284                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1285                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1286                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1287                                         ent->priv.server->suspendedinairflag = true;
1288                                 VectorClear (ent->fields.server->velocity);
1289                                 VectorClear (ent->fields.server->avelocity);
1290                         }
1291                         else
1292                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1293                 }
1294         }
1295
1296 // check for in water
1297         SV_CheckWaterTransition (ent);
1298 }
1299
1300 /*
1301 ===============================================================================
1302
1303 STEPPING MOVEMENT
1304
1305 ===============================================================================
1306 */
1307
1308 /*
1309 =============
1310 SV_Physics_Step
1311
1312 Monsters freefall when they don't have a ground entity, otherwise
1313 all movement is done with discrete steps.
1314
1315 This is also used for objects that have become still on the ground, but
1316 will fall if the floor is pulled out from under them.
1317 =============
1318 */
1319 void SV_Physics_Step (prvm_edict_t *ent)
1320 {
1321         int flags = (int)ent->fields.server->flags;
1322         // don't fall at all if fly/swim
1323         if (!(flags & (FL_FLY | FL_SWIM)))
1324         {
1325                 if (flags & FL_ONGROUND)
1326                 {
1327                         // freefall if onground and moving upward
1328                         // freefall if not standing on a world surface (it may be a lift)
1329                         prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1330                         if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1331                         {
1332                                 ent->fields.server->flags -= FL_ONGROUND;
1333                                 SV_AddGravity(ent);
1334                                 SV_CheckVelocity(ent);
1335                                 SV_FlyMove(ent, sv.frametime, NULL);
1336                                 SV_LinkEdict(ent, true);
1337                         }
1338                 }
1339                 else
1340                 {
1341                         // freefall if not onground
1342                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1343
1344                         SV_AddGravity(ent);
1345                         SV_CheckVelocity(ent);
1346                         SV_FlyMove(ent, sv.frametime, NULL);
1347                         SV_LinkEdict(ent, true);
1348
1349                         // just hit ground
1350                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && gamemode != GAME_NEXUIZ)
1351                                 SV_StartSound(ent, 0, "demon/dland2.wav", 255, 1);
1352                 }
1353         }
1354
1355 // regular thinking
1356         SV_RunThink(ent);
1357
1358         SV_CheckWaterTransition(ent);
1359 }
1360
1361 //============================================================================
1362
1363 static void SV_Physics_Entity (prvm_edict_t *ent, qboolean runmove)
1364 {
1365         switch ((int) ent->fields.server->movetype)
1366         {
1367         case MOVETYPE_PUSH:
1368         case MOVETYPE_FAKEPUSH:
1369                 SV_Physics_Pusher (ent);
1370                 break;
1371         case MOVETYPE_NONE:
1372                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1373                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1374                         SV_RunThink (ent);
1375                 break;
1376         case MOVETYPE_FOLLOW:
1377                 SV_Physics_Follow (ent);
1378                 break;
1379         case MOVETYPE_NOCLIP:
1380                 if (SV_RunThink(ent))
1381                 {
1382                         SV_CheckWater(ent);
1383                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1384                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1385                 }
1386                 SV_LinkEdict(ent, false);
1387                 break;
1388         case MOVETYPE_STEP:
1389                 SV_Physics_Step (ent);
1390                 break;
1391         case MOVETYPE_WALK:
1392                 if (SV_RunThink (ent))
1393                 {
1394                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1395                                 SV_AddGravity (ent);
1396                         SV_CheckStuck (ent);
1397                         SV_WalkMove (ent);
1398                         SV_LinkEdict (ent, true);
1399                 }
1400                 break;
1401         case MOVETYPE_TOSS:
1402         case MOVETYPE_BOUNCE:
1403         case MOVETYPE_BOUNCEMISSILE:
1404         case MOVETYPE_FLYMISSILE:
1405         case MOVETYPE_FLY:
1406                 // regular thinking
1407                 if (SV_RunThink (ent) && runmove)
1408                         SV_Physics_Toss (ent);
1409                 break;
1410         default:
1411                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1412                 break;
1413         }
1414 }
1415
1416 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1417 {
1418         // make sure the velocity is sane (not a NaN)
1419         SV_CheckVelocity(ent);
1420         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1421         if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1422         {
1423                 prog->globals.server->time = sv.time;
1424                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1425                 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1426         }
1427         else
1428                 SV_ClientThink ();
1429         // make sure the velocity is sane (not a NaN)
1430         SV_CheckVelocity(ent);
1431         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1432         // player_run/player_stand1 does not horribly malfunction if the
1433         // velocity becomes a number that is both == 0 and != 0
1434         // (sounds to me like NaN but to be absolutely safe...)
1435         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1436                 VectorClear(ent->fields.server->velocity);
1437         // call standard client pre-think
1438         prog->globals.server->time = sv.time;
1439         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1440         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1441         SV_CheckVelocity (ent);
1442
1443         switch ((int) ent->fields.server->movetype)
1444         {
1445         case MOVETYPE_PUSH:
1446         case MOVETYPE_FAKEPUSH:
1447                 SV_Physics_Pusher (ent);
1448                 break;
1449         case MOVETYPE_NONE:
1450                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1451                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1452                         SV_RunThink (ent);
1453                 break;
1454         case MOVETYPE_FOLLOW:
1455                 SV_Physics_Follow (ent);
1456                 break;
1457         case MOVETYPE_NOCLIP:
1458                 if (SV_RunThink(ent))
1459                 {
1460                         SV_CheckWater(ent);
1461                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1462                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1463                 }
1464                 break;
1465         case MOVETYPE_STEP:
1466                 SV_Physics_Step (ent);
1467                 break;
1468         case MOVETYPE_WALK:
1469                 if (SV_RunThink (ent))
1470                 {
1471                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1472                                 SV_AddGravity (ent);
1473                         SV_CheckStuck (ent);
1474                         SV_WalkMove (ent);
1475                 }
1476                 break;
1477         case MOVETYPE_TOSS:
1478         case MOVETYPE_BOUNCE:
1479         case MOVETYPE_BOUNCEMISSILE:
1480         case MOVETYPE_FLYMISSILE:
1481                 // regular thinking
1482                 if (SV_RunThink (ent))
1483                         SV_Physics_Toss (ent);
1484                 break;
1485         case MOVETYPE_FLY:
1486                 if (SV_RunThink (ent))
1487                 {
1488                         SV_CheckWater (ent);
1489                         SV_WalkMove (ent);
1490                 }
1491                 break;
1492         default:
1493                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1494                 break;
1495         }
1496
1497         SV_CheckVelocity (ent);
1498
1499         // call standard player post-think
1500         SV_LinkEdict (ent, true);
1501
1502         SV_CheckVelocity (ent);
1503
1504         prog->globals.server->time = sv.time;
1505         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1506         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1507 }
1508
1509 /*
1510 ================
1511 SV_Physics
1512
1513 ================
1514 */
1515 void SV_Physics (void)
1516 {
1517         int i, newnum_edicts;
1518         prvm_edict_t *ent;
1519         unsigned char runmove[MAX_EDICTS];
1520
1521 // let the progs know that a new frame has started
1522         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1523         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1524         prog->globals.server->time = sv.time;
1525         prog->globals.server->frametime = sv.frametime;
1526         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1527
1528         // don't run a move on newly spawned projectiles as it messes up movement
1529         // interpolation and rocket trails
1530         newnum_edicts = 0;
1531         for (i = 0, ent = prog->edicts;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1532                 if ((runmove[i] = !ent->priv.server->free))
1533                         newnum_edicts = i + 1;
1534         prog->num_edicts = max(svs.maxclients + 1, newnum_edicts);
1535
1536 //
1537 // treat each object in turn
1538 //
1539
1540         // if force_retouch, relink all the entities
1541         if (prog->globals.server->force_retouch > 0)
1542                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1543                         if (!ent->priv.server->free)
1544                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
1545
1546         // run physics on the client entities
1547         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1548         {
1549                 if (!ent->priv.server->free)
1550                 {
1551                         // don't do physics on disconnected clients, FrikBot relies on this
1552                         if (!host_client->spawned)
1553                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1554                         // don't run physics here if running asynchronously
1555                         else if (!host_client->movesequence)
1556                                 SV_Physics_ClientEntity(ent);
1557                 }
1558         }
1559
1560         // run physics on all the non-client entities
1561         if (!sv_freezenonclients.integer)
1562                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1563                         if (!ent->priv.server->free)
1564                                 SV_Physics_Entity(ent, runmove[i]);
1565
1566         if (prog->globals.server->force_retouch > 0)
1567                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1568
1569         // LordHavoc: endframe support
1570         if (EndFrameQC)
1571         {
1572                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1573                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1574                 prog->globals.server->time = sv.time;
1575                 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1576         }
1577
1578         if (!sv_freezenonclients.integer)
1579                 sv.time += sv.frametime;
1580 }
1581
1582
1583 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1584 {
1585         int i;
1586         float gravity, savesolid;
1587         vec3_t move, end;
1588         prvm_edict_t tempent, *tent;
1589         entvars_t vars;
1590         prvm_eval_t *val;
1591         trace_t trace;
1592
1593         // copy the vars over
1594         memcpy(&vars, tossent->fields.server, sizeof(entvars_t));
1595         // set up the temp entity to point to the copied vars
1596         tent = &tempent;
1597         tent->fields.server = &vars;
1598
1599         savesolid = tossent->fields.server->solid;
1600         tossent->fields.server->solid = SOLID_NOT;
1601
1602         // this has to fetch the field from the original edict, since our copy is truncated
1603         val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1604         if (val != NULL && val->_float != 0)
1605                 gravity = val->_float;
1606         else
1607                 gravity = 1.0;
1608         gravity *= sv_gravity.value * 0.05;
1609
1610         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1611         {
1612                 SV_CheckVelocity (tent);
1613                 tent->fields.server->velocity[2] -= gravity;
1614                 VectorMA (tent->fields.server->angles, 0.05, tent->fields.server->avelocity, tent->fields.server->angles);
1615                 VectorScale (tent->fields.server->velocity, 0.05, move);
1616                 VectorAdd (tent->fields.server->origin, move, end);
1617                 trace = SV_Move (tent->fields.server->origin, tent->fields.server->mins, tent->fields.server->maxs, end, MOVE_NORMAL, tent);
1618                 VectorCopy (trace.endpos, tent->fields.server->origin);
1619
1620                 if (trace.fraction < 1 && trace.ent && trace.ent != ignore)
1621                         break;
1622         }
1623         tossent->fields.server->solid = savesolid;
1624         trace.fraction = 0; // not relevant
1625         return trace;
1626 }
1627