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