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