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