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