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