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