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