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