]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_phys.c
added svc_trailparticles and svc_pointparticles, these are written by the trailpartic...
[divverent/darkplaces.git] / sv_phys.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // sv_phys.c
21
22 #include "quakedef.h"
23
24 /*
25
26
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
28
29 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38 solid_edge items only clip against bsp models.
39
40 */
41
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
43 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
44 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
45 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
46 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
47 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
48 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
49 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
50 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
51 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
52 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
53 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
54 cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
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 // TODO: move this extern to server.h
60 extern cvar_t sv_clmovement_waitforinput;
61
62 #define MOVE_EPSILON    0.01
63
64 void SV_Physics_Toss (prvm_edict_t *ent);
65
66 void SV_Phys_Init (void)
67 {
68         Cvar_RegisterVariable(&sv_stepheight);
69         Cvar_RegisterVariable(&sv_jumpstep);
70         Cvar_RegisterVariable(&sv_wallfriction);
71         Cvar_RegisterVariable(&sv_newflymove);
72         Cvar_RegisterVariable(&sv_freezenonclients);
73         Cvar_RegisterVariable(&sv_playerphysicsqc);
74         Cvar_RegisterVariable(&sv_debugmove);
75
76         Cvar_RegisterVariable(&sv_sound_watersplash);
77         Cvar_RegisterVariable(&sv_sound_land);
78 }
79
80 /*
81 ===============================================================================
82
83 LINE TESTING IN HULLS
84
85 ===============================================================================
86 */
87
88 /*
89 ==================
90 SV_Move_ClipToEntity
91
92 Handles selection or creation of a clipping hull, and offseting (and
93 eventually rotation) of the end points
94 ==================
95 */
96 trace_t SV_Move_ClipToEntity(prvm_edict_t *ent, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int movetype, int hitsupercontents)
97 {
98         trace_t trace;
99         model_t *model = NULL;
100         matrix4x4_t matrix, imatrix;
101         float tempnormal[3], starttransformed[3], endtransformed[3];
102
103         memset(&trace, 0, sizeof(trace));
104         trace.fraction = trace.realfraction = 1;
105         VectorCopy(end, trace.endpos);
106
107         if ((int) ent->fields.server->solid == SOLID_BSP || movetype == MOVE_HITMODEL)
108         {
109                 unsigned int modelindex = (unsigned int)ent->fields.server->modelindex;
110                 // if the modelindex is 0, it shouldn't be SOLID_BSP!
111                 if (modelindex == 0)
112                 {
113                         Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with no model\n", PRVM_NUM_FOR_EDICT(ent));
114                         return trace;
115                 }
116                 if (modelindex >= MAX_MODELS)
117                 {
118                         Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent));
119                         return trace;
120                 }
121                 model = sv.models[modelindex];
122                 if (modelindex != 0 && model == NULL)
123                 {
124                         Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent));
125                         return trace;
126                 }
127
128                 if ((int) ent->fields.server->solid == SOLID_BSP)
129                 {
130                         if (!model->TraceBox)
131                         {
132                                 Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with a non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
133                                 return trace;
134                         }
135                         //if ((int) ent->fields.server->movetype != MOVETYPE_PUSH)
136                         //{
137                         //      Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP without MOVETYPE_PUSH\n", PRVM_NUM_FOR_EDICT(ent));
138                         //      return trace;
139                         //}
140                 }
141                 Matrix4x4_CreateFromQuakeEntity(&matrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2], ent->fields.server->angles[0], ent->fields.server->angles[1], ent->fields.server->angles[2], 1);
142         }
143         else
144                 Matrix4x4_CreateTranslate(&matrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
145
146         Matrix4x4_Invert_Simple(&imatrix, &matrix);
147         Matrix4x4_Transform(&imatrix, start, starttransformed);
148         Matrix4x4_Transform(&imatrix, end, endtransformed);
149 #if COLLISIONPARANOID >= 3
150         Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]);
151 #endif
152
153         if (model && model->TraceBox)
154         {
155                 int frame;
156                 frame = (int)ent->fields.server->frame;
157                 frame = bound(0, frame, (model->numframes - 1));
158                 model->TraceBox(model, frame, &trace, starttransformed, mins, maxs, endtransformed, hitsupercontents);
159         }
160         else
161                 Collision_ClipTrace_Box(&trace, ent->fields.server->mins, ent->fields.server->maxs, starttransformed, mins, maxs, endtransformed, hitsupercontents, ent->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY, 0, NULL);
162         trace.fraction = bound(0, trace.fraction, 1);
163         trace.realfraction = bound(0, trace.realfraction, 1);
164
165         if (trace.fraction < 1)
166         {
167                 VectorLerp(start, trace.fraction, end, trace.endpos);
168                 VectorCopy(trace.plane.normal, tempnormal);
169                 Matrix4x4_Transform3x3(&matrix, tempnormal, trace.plane.normal);
170                 // FIXME: should recalc trace.plane.dist
171         }
172         else
173                 VectorCopy(end, trace.endpos);
174
175         return trace;
176 }
177
178 /*
179 ==================
180 SV_Move_ClipToWorld
181 ==================
182 */
183 trace_t SV_Move_ClipToWorld(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int movetype, int hitsupercontents)
184 {
185         trace_t trace;
186         memset(&trace, 0, sizeof(trace));
187         trace.fraction = trace.realfraction = 1;
188         sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, start, mins, maxs, end, hitsupercontents);
189         trace.fraction = bound(0, trace.fraction, 1);
190         trace.realfraction = bound(0, trace.realfraction, 1);
191         VectorLerp(start, trace.fraction, end, trace.endpos);
192         return trace;
193 }
194
195 /*
196 ==================
197 SV_Move
198 ==================
199 */
200 #if COLLISIONPARANOID >= 1
201 trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict)
202 #else
203 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict)
204 #endif
205 {
206         vec3_t hullmins, hullmaxs;
207         int i;
208         int hitsupercontentsmask;
209         int passedictprog;
210         qboolean pointtrace;
211         prvm_edict_t *traceowner, *touch;
212         prvm_eval_t *val;
213         trace_t trace;
214         // bounding box of entire move area
215         vec3_t clipboxmins, clipboxmaxs;
216         // size of the moving object
217         vec3_t clipmins, clipmaxs;
218         // size when clipping against monsters
219         vec3_t clipmins2, clipmaxs2;
220         // start and end origin of move
221         vec3_t clipstart, clipend;
222         // trace results
223         trace_t cliptrace;
224         int numtouchedicts;
225         prvm_edict_t *touchedicts[MAX_EDICTS];
226
227         VectorCopy(start, clipstart);
228         VectorCopy(end, clipend);
229         VectorCopy(mins, clipmins);
230         VectorCopy(maxs, clipmaxs);
231         VectorCopy(mins, clipmins2);
232         VectorCopy(maxs, clipmaxs2);
233 #if COLLISIONPARANOID >= 3
234         Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
235 #endif
236
237         if (passedict)
238         {
239                 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
240                 if (val && val->_float)
241                         hitsupercontentsmask = (int)val->_float;
242                 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
243                 {
244                         if ((int)passedict->fields.server->flags & FL_MONSTER)
245                                 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
246                         else
247                                 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
248                 }
249                 else if (passedict->fields.server->solid == SOLID_CORPSE)
250                         hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
251                 else
252                         hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
253         }
254         else
255                 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
256
257         // clip to world
258         cliptrace = SV_Move_ClipToWorld(clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask);
259         cliptrace.bmodelstartsolid = cliptrace.startsolid;
260         if (cliptrace.startsolid || cliptrace.fraction < 1)
261                 cliptrace.ent = prog->edicts;
262         if (type == MOVE_WORLDONLY)
263                 return cliptrace;
264
265         if (type == MOVE_MISSILE)
266         {
267                 // LordHavoc: modified this, was = -15, now -= 15
268                 for (i = 0;i < 3;i++)
269                 {
270                         clipmins2[i] -= 15;
271                         clipmaxs2[i] += 15;
272                 }
273         }
274
275         // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
276         if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
277                 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
278         else
279         {
280                 VectorCopy(clipmins, hullmins);
281                 VectorCopy(clipmaxs, hullmaxs);
282         }
283
284         // create the bounding box of the entire move
285         for (i = 0;i < 3;i++)
286         {
287                 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
288                 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
289         }
290
291         // debug override to test against everything
292         if (sv_debugmove.integer)
293         {
294                 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
295                 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] =  999999999;
296         }
297
298         // if the passedict is world, make it NULL (to avoid two checks each time)
299         if (passedict == prog->edicts)
300                 passedict = NULL;
301         // precalculate prog value for passedict for comparisons
302         passedictprog = PRVM_EDICT_TO_PROG(passedict);
303         // figure out whether this is a point trace for comparisons
304         pointtrace = VectorCompare(clipmins, clipmaxs);
305         // precalculate passedict's owner edict pointer for comparisons
306         traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
307
308         // clip to entities
309         numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
310         if (numtouchedicts > MAX_EDICTS)
311         {
312                 // this never happens
313                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
314                 numtouchedicts = MAX_EDICTS;
315         }
316         for (i = 0;i < numtouchedicts;i++)
317         {
318                 touch = touchedicts[i];
319
320                 if (touch->fields.server->solid < SOLID_BBOX)
321                         continue;
322                 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
323                         continue;
324
325                 if (passedict)
326                 {
327                         // don't clip against self
328                         if (passedict == touch)
329                                 continue;
330                         // don't clip owned entities against owner
331                         if (traceowner == touch)
332                                 continue;
333                         // don't clip owner against owned entities
334                         if (passedictprog == touch->fields.server->owner)
335                                 continue;
336                         // don't clip points against points (they can't collide)
337                         if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
338                                 continue;
339                 }
340
341                 // might interact, so do an exact clip
342                 if ((int)touch->fields.server->flags & FL_MONSTER)
343                         trace = SV_Move_ClipToEntity(touch, clipstart, clipmins2, clipmaxs2, clipend, type, hitsupercontentsmask);
344                 else
345                         trace = SV_Move_ClipToEntity(touch, clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask);
346                 // LordHavoc: take the 'best' answers from the new trace and combine with existing data
347                 if (trace.allsolid)
348                         cliptrace.allsolid = true;
349                 if (trace.startsolid)
350                 {
351                         if (touch->fields.server->solid == SOLID_BSP)
352                                 cliptrace.bmodelstartsolid = true;
353                         cliptrace.startsolid = true;
354                         if (cliptrace.realfraction == 1)
355                                 cliptrace.ent = touch;
356                 }
357                 // don't set this except on the world, because it can easily confuse
358                 // monsters underwater if there's a bmodel involved in the trace
359                 // (inopen && inwater is how they check water visibility)
360                 //if (trace.inopen)
361                 //      cliptrace.inopen = true;
362                 if (trace.inwater)
363                         cliptrace.inwater = true;
364                 if (trace.realfraction < cliptrace.realfraction)
365                 {
366                         cliptrace.fraction = trace.fraction;
367                         cliptrace.realfraction = trace.realfraction;
368                         VectorCopy(trace.endpos, cliptrace.endpos);
369                         cliptrace.plane = trace.plane;
370                         cliptrace.ent = touch;
371                         cliptrace.hitsupercontents = trace.hitsupercontents;
372                         cliptrace.hitq3surfaceflags = trace.hitq3surfaceflags;
373                         cliptrace.hittexture = trace.hittexture;
374                 }
375                 cliptrace.startsupercontents |= trace.startsupercontents;
376         }
377
378         return cliptrace;
379 }
380
381 #if COLLISIONPARANOID >= 1
382 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict)
383 {
384         int endstuck;
385         trace_t trace;
386         vec3_t temp;
387         trace = SV_Move_(start, mins, maxs, end, type, passedict);
388         if (passedict)
389         {
390                 VectorCopy(trace.endpos, temp);
391                 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict).startsolid;
392 #if COLLISIONPARANOID < 3
393                 if (trace.startsolid || endstuck)
394 #endif
395                         Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "^3" : "", passedict ? (int)(passedict - prog->edicts) : -1, passedict->fields.server->origin[0], passedict->fields.server->origin[1], passedict->fields.server->origin[2], end[0] - passedict->fields.server->origin[0], end[1] - passedict->fields.server->origin[1], end[2] - passedict->fields.server->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.server->origin[0], trace.endpos[1] - passedict->fields.server->origin[1], trace.endpos[2] - passedict->fields.server->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : "");
396         }
397         return trace;
398 }
399 #endif
400
401 /*
402 ===============================================================================
403
404 Linking entities into the world culling system
405
406 ===============================================================================
407 */
408
409 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
410 {
411         int i, numtouchedicts, old_self, old_other;
412         prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
413
414         // build a list of edicts to touch, because the link loop can be corrupted
415         // by SV_IncreaseEdicts called during touch functions
416         numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
417         if (numtouchedicts > MAX_EDICTS)
418         {
419                 // this never happens
420                 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
421                 numtouchedicts = MAX_EDICTS;
422         }
423
424         old_self = prog->globals.server->self;
425         old_other = prog->globals.server->other;
426         for (i = 0;i < numtouchedicts;i++)
427         {
428                 touch = touchedicts[i];
429                 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
430                 {
431                         prvm_eval_t *val;
432                         prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
433                         prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
434                         prog->globals.server->time = sv.time;
435                         prog->globals.server->trace_allsolid = false;
436                         prog->globals.server->trace_startsolid = false;
437                         prog->globals.server->trace_fraction = 1;
438                         prog->globals.server->trace_inwater = false;
439                         prog->globals.server->trace_inopen = true;
440                         VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
441                         VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
442                         prog->globals.server->trace_plane_dist = 0;
443                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
444                         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
445                                 val->_float = 0;
446                         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
447                                 val->_float = 0;
448                         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
449                                 val->_float = 0;
450                         if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
451                                 val->string = 0;
452                         PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
453                 }
454         }
455         prog->globals.server->self = old_self;
456         prog->globals.server->other = old_other;
457 }
458
459 /*
460 ===============
461 SV_LinkEdict
462
463 ===============
464 */
465 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
466 {
467         model_t *model;
468         vec3_t mins, maxs;
469
470         if (ent == prog->edicts)
471                 return;         // don't add the world
472
473         if (ent->priv.server->free)
474                 return;
475
476 // set the abs box
477
478         if (ent->fields.server->solid == SOLID_BSP)
479         {
480                 int modelindex = (int)ent->fields.server->modelindex;
481                 if (modelindex < 0 || modelindex > MAX_MODELS)
482                 {
483                         Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
484                         modelindex = 0;
485                 }
486                 model = sv.models[modelindex];
487                 if (model != NULL)
488                 {
489                         if (!model->TraceBox)
490                                 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
491
492                         if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
493                         {
494                                 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
495                                 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
496                         }
497                         else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
498                         {
499                                 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
500                                 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
501                         }
502                         else
503                         {
504                                 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
505                                 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
506                         }
507                 }
508                 else
509                 {
510                         // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
511                         VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
512                         VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
513                 }
514         }
515         else
516         {
517                 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
518                 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
519         }
520
521 //
522 // to make items easier to pick up and allow them to be grabbed off
523 // of shelves, the abs sizes are expanded
524 //
525         if ((int)ent->fields.server->flags & FL_ITEM)
526         {
527                 mins[0] -= 15;
528                 mins[1] -= 15;
529                 mins[2] -= 1;
530                 maxs[0] += 15;
531                 maxs[1] += 15;
532                 maxs[2] += 1;
533         }
534         else
535         {
536                 // because movement is clipped an epsilon away from an actual edge,
537                 // we must fully check even when bounding boxes don't quite touch
538                 mins[0] -= 1;
539                 mins[1] -= 1;
540                 mins[2] -= 1;
541                 maxs[0] += 1;
542                 maxs[1] += 1;
543                 maxs[2] += 1;
544         }
545
546         VectorCopy(mins, ent->fields.server->absmin);
547         VectorCopy(maxs, ent->fields.server->absmax);
548
549         World_LinkEdict(&sv.world, ent, mins, maxs);
550
551         // if touch_triggers, call touch on all entities overlapping this box
552         if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
553                 SV_LinkEdict_TouchAreaGrid(ent);
554 }
555
556 /*
557 ===============================================================================
558
559 Utility functions
560
561 ===============================================================================
562 */
563
564 /*
565 ============
566 SV_TestEntityPosition
567
568 returns true if the entity is in solid currently
569 ============
570 */
571 static int SV_TestEntityPosition (prvm_edict_t *ent)
572 {
573         trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
574         if (trace.startsupercontents & SUPERCONTENTS_SOLID)
575                 return true;
576         else
577         {
578                 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
579                 {
580                         // q1bsp/hlbsp use hulls and if the entity does not exactly match
581                         // a hull size it is incorrectly tested, so this code tries to
582                         // 'fix' it slightly...
583                         int i;
584                         vec3_t v, m1, m2, s;
585                         VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
586                         VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
587                         VectorSubtract(m2, m1, s);
588 #define EPSILON (1.0f / 32.0f)
589                         if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
590                         if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
591                         if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
592                         for (i = 0;i < 8;i++)
593                         {
594                                 v[0] = (i & 1) ? m2[0] : m1[0];
595                                 v[1] = (i & 2) ? m2[1] : m1[1];
596                                 v[2] = (i & 4) ? m2[2] : m1[2];
597                                 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
598                                         return true;
599                         }
600                 }
601                 return false;
602         }
603 }
604
605 /*
606 ================
607 SV_CheckAllEnts
608 ================
609 */
610 void SV_CheckAllEnts (void)
611 {
612         int e;
613         prvm_edict_t *check;
614
615         // see if any solid entities are inside the final position
616         check = PRVM_NEXT_EDICT(prog->edicts);
617         for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
618         {
619                 if (check->priv.server->free)
620                         continue;
621                 if (check->fields.server->movetype == MOVETYPE_PUSH
622                  || check->fields.server->movetype == MOVETYPE_NONE
623                  || check->fields.server->movetype == MOVETYPE_FOLLOW
624                  || check->fields.server->movetype == MOVETYPE_NOCLIP)
625                         continue;
626
627                 if (SV_TestEntityPosition (check))
628                         Con_Print("entity in invalid position\n");
629         }
630 }
631
632 // DRESK - Support for Entity Contents Transition Event
633 /*
634 ================
635 SV_CheckContentsTransition
636
637 returns true if entity had a valid contentstransition function call
638 ================
639 */
640 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
641 {
642         int bValidFunctionCall;
643         prvm_eval_t *contentstransition;
644
645         // Default Valid Function Call to False
646         bValidFunctionCall = false;
647
648         if(ent->fields.server->watertype != nContents)
649         { // Changed Contents
650                 // Acquire Contents Transition Function from QC
651                 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
652
653                 if(contentstransition->function)
654                 { // Valid Function; Execute
655                         // Assign Valid Function
656                         bValidFunctionCall = true;
657                         // Prepare Parameters (Original Contents, New Contents)
658                                 // Original Contents
659                                 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
660                                 // New Contents
661                                 PRVM_G_FLOAT(OFS_PARM1) = nContents;
662                         // Execute VM Function
663                         PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
664                 }
665         }
666
667         // Return if Function Call was Valid
668         return bValidFunctionCall;
669 }
670
671
672 /*
673 ================
674 SV_CheckVelocity
675 ================
676 */
677 void SV_CheckVelocity (prvm_edict_t *ent)
678 {
679         int i;
680         float wishspeed;
681
682 //
683 // bound velocity
684 //
685         for (i=0 ; i<3 ; i++)
686         {
687                 if (IS_NAN(ent->fields.server->velocity[i]))
688                 {
689                         Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
690                         ent->fields.server->velocity[i] = 0;
691                 }
692                 if (IS_NAN(ent->fields.server->origin[i]))
693                 {
694                         Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
695                         ent->fields.server->origin[i] = 0;
696                 }
697         }
698
699         // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
700         wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
701         if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
702         {
703                 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
704                 ent->fields.server->velocity[0] *= wishspeed;
705                 ent->fields.server->velocity[1] *= wishspeed;
706                 ent->fields.server->velocity[2] *= wishspeed;
707         }
708 }
709
710 /*
711 =============
712 SV_RunThink
713
714 Runs thinking code if time.  There is some play in the exact time the think
715 function will be called, because it is called before any movement is done
716 in a frame.  Not used for pushmove objects, because they must be exact.
717 Returns false if the entity removed itself.
718 =============
719 */
720 qboolean SV_RunThink (prvm_edict_t *ent)
721 {
722         float thinktime;
723
724         thinktime = ent->fields.server->nextthink;
725         if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
726                 return true;
727
728         // don't let things stay in the past.
729         // it is possible to start that way by a trigger with a local time.
730         if (thinktime < sv.time)
731                 thinktime = sv.time;
732
733         ent->fields.server->nextthink = 0;
734         prog->globals.server->time = thinktime;
735         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
736         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
737         PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
738         return !ent->priv.server->free;
739 }
740
741 /*
742 ==================
743 SV_Impact
744
745 Two entities have touched, so run their touch functions
746 ==================
747 */
748 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
749 {
750         int old_self, old_other;
751         prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
752         prvm_eval_t *val;
753
754         old_self = prog->globals.server->self;
755         old_other = prog->globals.server->other;
756
757         prog->globals.server->time = sv.time;
758         if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
759         {
760                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
761                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
762                 prog->globals.server->trace_allsolid = trace->allsolid;
763                 prog->globals.server->trace_startsolid = trace->startsolid;
764                 prog->globals.server->trace_fraction = trace->fraction;
765                 prog->globals.server->trace_inwater = trace->inwater;
766                 prog->globals.server->trace_inopen = trace->inopen;
767                 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
768                 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
769                 prog->globals.server->trace_plane_dist =  trace->plane.dist;
770                 if (trace->ent)
771                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
772                 else
773                         prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
774                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
775                         val->_float = trace->startsupercontents;
776                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
777                         val->_float = trace->hitsupercontents;
778                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
779                         val->_float = trace->hitq3surfaceflags;
780                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
781                 {
782                         if (trace->hittexture)
783                                 val->string = PRVM_SetTempString(trace->hittexture->name);
784                         else
785                                 val->string = 0;
786                 }
787                 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
788         }
789
790         if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
791         {
792                 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
793                 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
794                 prog->globals.server->trace_allsolid = false;
795                 prog->globals.server->trace_startsolid = false;
796                 prog->globals.server->trace_fraction = 1;
797                 prog->globals.server->trace_inwater = false;
798                 prog->globals.server->trace_inopen = true;
799                 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
800                 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
801                 prog->globals.server->trace_plane_dist = 0;
802                 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
803                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
804                         val->_float = 0;
805                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
806                         val->_float = 0;
807                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
808                         val->_float = 0;
809                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
810                         val->string = 0;
811                 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
812         }
813
814         prog->globals.server->self = old_self;
815         prog->globals.server->other = old_other;
816 }
817
818
819 /*
820 ==================
821 ClipVelocity
822
823 Slide off of the impacting object
824 returns the blocked flags (1 = floor, 2 = step / wall)
825 ==================
826 */
827 #define STOP_EPSILON 0.1
828 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
829 {
830         int i;
831         float backoff;
832
833         backoff = -DotProduct (in, normal) * overbounce;
834         VectorMA(in, backoff, normal, out);
835
836         for (i = 0;i < 3;i++)
837                 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
838                         out[i] = 0;
839 }
840
841
842 /*
843 ============
844 SV_FlyMove
845
846 The basic solid body movement clip that slides along multiple planes
847 Returns the clipflags if the velocity was modified (hit something solid)
848 1 = floor
849 2 = wall / step
850 4 = dead stop
851 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
852 ============
853 */
854 // LordHavoc: increased from 5 to 32
855 #define MAX_CLIP_PLANES 32
856 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
857 {
858         int blocked, bumpcount;
859         int i, j, impact, numplanes;
860         float d, time_left;
861         vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
862         trace_t trace;
863         if (time <= 0)
864                 return 0;
865         blocked = 0;
866         VectorCopy(ent->fields.server->velocity, original_velocity);
867         VectorCopy(ent->fields.server->velocity, primal_velocity);
868         numplanes = 0;
869         time_left = time;
870         for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
871         {
872                 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
873                         break;
874
875                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
876                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
877 #if 0
878                 //if (trace.fraction < 0.002)
879                 {
880 #if 1
881                         vec3_t start;
882                         trace_t testtrace;
883                         VectorCopy(ent->fields.server->origin, start);
884                         start[2] += 3;//0.03125;
885                         VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
886                         end[2] += 3;//0.03125;
887                         testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
888                         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)))
889                         {
890                                 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
891                                 trace = testtrace;
892                         }
893 #endif
894 #if 0
895                         //j = -1;
896                         for (i = 0;i < numplanes;i++)
897                         {
898                                 VectorCopy(ent->fields.server->origin, start);
899                                 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
900                                 VectorMA(start, 3, planes[i], start);
901                                 VectorMA(end, 3, planes[i], end);
902                                 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
903                                 if (trace.fraction < testtrace.fraction)
904                                 {
905                                         trace = testtrace;
906                                         VectorCopy(start, ent->fields.server->origin);
907                                         //j = i;
908                                 }
909                         }
910                         //if (j >= 0)
911                         //      VectorAdd(ent->fields.server->origin, planes[j], start);
912 #endif
913                 }
914 #endif
915
916 #if 0
917                 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);
918                 if (trace.fraction < 1)
919                         Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
920                 Con_Print("\n");
921 #endif
922
923 #if 0
924                 if (trace.bmodelstartsolid)
925                 {
926                         // LordHavoc: note: this code is what makes entities stick in place
927                         // if embedded in world only (you can walk through other objects if
928                         // stuck)
929                         // entity is trapped in another solid
930                         VectorClear(ent->fields.server->velocity);
931                         return 3;
932                 }
933 #endif
934
935                 // break if it moved the entire distance
936                 if (trace.fraction == 1)
937                 {
938                         VectorCopy(trace.endpos, ent->fields.server->origin);
939                         break;
940                 }
941
942                 if (!trace.ent)
943                 {
944                         Con_Printf ("SV_FlyMove: !trace.ent");
945                         trace.ent = prog->edicts;
946                 }
947
948                 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
949                         impact = false;
950                 else
951                 {
952                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
953                         impact = true;
954                 }
955
956                 if (trace.plane.normal[2])
957                 {
958                         if (trace.plane.normal[2] > 0.7)
959                         {
960                                 // floor
961                                 blocked |= 1;
962                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
963                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
964                         }
965                 }
966                 else
967                 {
968                         // step
969                         blocked |= 2;
970                         // save the trace for player extrafriction
971                         if (stepnormal)
972                                 VectorCopy(trace.plane.normal, stepnormal);
973                 }
974
975                 if (trace.fraction >= 0.001)
976                 {
977                         // actually covered some distance
978                         VectorCopy(trace.endpos, ent->fields.server->origin);
979                         VectorCopy(ent->fields.server->velocity, original_velocity);
980                         numplanes = 0;
981                 }
982
983                 // run the impact function
984                 if (impact)
985                 {
986                         SV_Impact(ent, &trace);
987
988                         // break if removed by the impact function
989                         if (ent->priv.server->free)
990                                 break;
991                 }
992
993                 time_left *= 1 - trace.fraction;
994
995                 // clipped to another plane
996                 if (numplanes >= MAX_CLIP_PLANES)
997                 {
998                         // this shouldn't really happen
999                         VectorClear(ent->fields.server->velocity);
1000                         blocked = 3;
1001                         break;
1002                 }
1003
1004                 /*
1005                 for (i = 0;i < numplanes;i++)
1006                         if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1007                                 break;
1008                 if (i < numplanes)
1009                 {
1010                         VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1011                         continue;
1012                 }
1013                 */
1014
1015                 VectorCopy(trace.plane.normal, planes[numplanes]);
1016                 numplanes++;
1017
1018                 if (sv_newflymove.integer)
1019                         ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1020                 else
1021                 {
1022                         // modify original_velocity so it parallels all of the clip planes
1023                         for (i = 0;i < numplanes;i++)
1024                         {
1025                                 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1026                                 for (j = 0;j < numplanes;j++)
1027                                 {
1028                                         if (j != i)
1029                                         {
1030                                                 // not ok
1031                                                 if (DotProduct(new_velocity, planes[j]) < 0)
1032                                                         break;
1033                                         }
1034                                 }
1035                                 if (j == numplanes)
1036                                         break;
1037                         }
1038
1039                         if (i != numplanes)
1040                         {
1041                                 // go along this plane
1042                                 VectorCopy(new_velocity, ent->fields.server->velocity);
1043                         }
1044                         else
1045                         {
1046                                 // go along the crease
1047                                 if (numplanes != 2)
1048                                 {
1049                                         VectorClear(ent->fields.server->velocity);
1050                                         blocked = 7;
1051                                         break;
1052                                 }
1053                                 CrossProduct(planes[0], planes[1], dir);
1054                                 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1055                                 VectorNormalize(dir);
1056                                 d = DotProduct(dir, ent->fields.server->velocity);
1057                                 VectorScale(dir, d, ent->fields.server->velocity);
1058                         }
1059                 }
1060
1061                 // if current velocity is against the original velocity,
1062                 // stop dead to avoid tiny occilations in sloping corners
1063                 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1064                 {
1065                         VectorClear(ent->fields.server->velocity);
1066                         break;
1067                 }
1068         }
1069
1070         //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]);
1071
1072         /*
1073         if ((blocked & 1) == 0 && bumpcount > 1)
1074         {
1075                 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1076                 // flag ONGROUND if there's ground under it
1077                 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
1078         }
1079         */
1080
1081         // LordHavoc: this came from QW and allows you to get out of water more easily
1082         if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
1083                 VectorCopy(primal_velocity, ent->fields.server->velocity);
1084         return blocked;
1085 }
1086
1087 /*
1088 ============
1089 SV_AddGravity
1090
1091 ============
1092 */
1093 void SV_AddGravity (prvm_edict_t *ent)
1094 {
1095         float ent_gravity;
1096         prvm_eval_t *val;
1097
1098         val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1099         if (val!=0 && val->_float)
1100                 ent_gravity = val->_float;
1101         else
1102                 ent_gravity = 1.0;
1103         ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
1104 }
1105
1106
1107 /*
1108 ===============================================================================
1109
1110 PUSHMOVE
1111
1112 ===============================================================================
1113 */
1114
1115 /*
1116 ============
1117 SV_PushEntity
1118
1119 Does not change the entities velocity at all
1120 ============
1121 */
1122 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
1123 {
1124         int type;
1125         trace_t trace;
1126         vec3_t end;
1127
1128         VectorAdd (ent->fields.server->origin, push, end);
1129
1130         if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1131                 type = MOVE_MISSILE;
1132         else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1133                 type = MOVE_NOMONSTERS; // only clip against bmodels
1134         else
1135                 type = MOVE_NORMAL;
1136
1137         trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
1138         if (trace.bmodelstartsolid && failonbmodelstartsolid)
1139                 return trace;
1140
1141         VectorCopy (trace.endpos, ent->fields.server->origin);
1142         SV_LinkEdict (ent, true);
1143
1144         if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
1145                 SV_Impact (ent, &trace);
1146         return trace;
1147 }
1148
1149
1150 /*
1151 ============
1152 SV_PushMove
1153
1154 ============
1155 */
1156 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1157 {
1158         int i, e, index;
1159         float savesolid, movetime2, pushltime;
1160         vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1161         int num_moved;
1162         int numcheckentities;
1163         static prvm_edict_t *checkentities[MAX_EDICTS];
1164         model_t *pushermodel;
1165         trace_t trace;
1166
1167         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])
1168         {
1169                 pusher->fields.server->ltime += movetime;
1170                 return;
1171         }
1172
1173         switch ((int) pusher->fields.server->solid)
1174         {
1175         // LordHavoc: valid pusher types
1176         case SOLID_BSP:
1177         case SOLID_BBOX:
1178         case SOLID_SLIDEBOX:
1179         case SOLID_CORPSE: // LordHavoc: this would be weird...
1180                 break;
1181         // LordHavoc: no collisions
1182         case SOLID_NOT:
1183         case SOLID_TRIGGER:
1184                 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1185                 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1186                 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1187                 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1188                 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1189                 pusher->fields.server->ltime += movetime;
1190                 SV_LinkEdict (pusher, false);
1191                 return;
1192         default:
1193                 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1194                 return;
1195         }
1196         index = (int) pusher->fields.server->modelindex;
1197         if (index < 1 || index >= MAX_MODELS)
1198         {
1199                 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1200                 return;
1201         }
1202         pushermodel = sv.models[index];
1203
1204         movetime2 = movetime;
1205         VectorScale(pusher->fields.server->velocity, movetime2, move1);
1206         VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1207         if (moveangle[0] || moveangle[2])
1208         {
1209                 for (i = 0;i < 3;i++)
1210                 {
1211                         if (move1[i] > 0)
1212                         {
1213                                 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1214                                 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1215                         }
1216                         else
1217                         {
1218                                 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1219                                 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1220                         }
1221                 }
1222         }
1223         else if (moveangle[1])
1224         {
1225                 for (i = 0;i < 3;i++)
1226                 {
1227                         if (move1[i] > 0)
1228                         {
1229                                 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1230                                 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1231                         }
1232                         else
1233                         {
1234                                 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1235                                 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1236                         }
1237                 }
1238         }
1239         else
1240         {
1241                 for (i = 0;i < 3;i++)
1242                 {
1243                         if (move1[i] > 0)
1244                         {
1245                                 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1246                                 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1247                         }
1248                         else
1249                         {
1250                                 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1251                                 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1252                         }
1253                 }
1254         }
1255
1256         VectorNegate (moveangle, a);
1257         AngleVectorsFLU (a, forward, left, up);
1258
1259         VectorCopy (pusher->fields.server->origin, pushorig);
1260         VectorCopy (pusher->fields.server->angles, pushang);
1261         pushltime = pusher->fields.server->ltime;
1262
1263 // move the pusher to its final position
1264
1265         VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1266         VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1267         pusher->fields.server->ltime += movetime;
1268         SV_LinkEdict (pusher, false);
1269
1270         savesolid = pusher->fields.server->solid;
1271
1272 // see if any solid entities are inside the final position
1273         num_moved = 0;
1274
1275         numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1276         for (e = 0;e < numcheckentities;e++)
1277         {
1278                 prvm_edict_t *check = checkentities[e];
1279                 if (check->fields.server->movetype == MOVETYPE_NONE
1280                  || check->fields.server->movetype == MOVETYPE_PUSH
1281                  || check->fields.server->movetype == MOVETYPE_FOLLOW
1282                  || check->fields.server->movetype == MOVETYPE_NOCLIP
1283                  || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1284                         continue;
1285
1286                 // if the entity is standing on the pusher, it will definitely be moved
1287                 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
1288                 {
1289                         // remove the onground flag for non-players
1290                         if (check->fields.server->movetype != MOVETYPE_WALK)
1291                                 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1292                 }
1293                 else
1294                 {
1295                         // if the entity is not inside the pusher's final position, leave it alone
1296                         if (!SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1297                                 continue;
1298                 }
1299
1300
1301                 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1302                 {
1303                         vec3_t org2;
1304                         VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1305                         org2[0] = DotProduct (org, forward);
1306                         org2[1] = DotProduct (org, left);
1307                         org2[2] = DotProduct (org, up);
1308                         VectorSubtract (org2, org, move);
1309                         VectorAdd (move, move1, move);
1310                 }
1311                 else
1312                         VectorCopy (move1, move);
1313
1314                 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1315                 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1316                 sv.moved_edicts[num_moved++] = check;
1317
1318                 // try moving the contacted entity
1319                 pusher->fields.server->solid = SOLID_NOT;
1320                 trace = SV_PushEntity (check, move, true);
1321                 // FIXME: turn players specially
1322                 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1323                 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1324                 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1325
1326                 // if it is still inside the pusher, block
1327                 if (SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1328                 {
1329                         // try moving the contacted entity a tiny bit further to account for precision errors
1330                         vec3_t move2;
1331                         pusher->fields.server->solid = SOLID_NOT;
1332                         VectorScale(move, 1.1, move2);
1333                         VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1334                         VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1335                         SV_PushEntity (check, move2, true);
1336                         pusher->fields.server->solid = savesolid;
1337                         if (SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1338                         {
1339                                 // try moving the contacted entity a tiny bit less to account for precision errors
1340                                 pusher->fields.server->solid = SOLID_NOT;
1341                                 VectorScale(move, 0.9, move2);
1342                                 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1343                                 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1344                                 SV_PushEntity (check, move2, true);
1345                                 pusher->fields.server->solid = savesolid;
1346                                 if (SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1347                                 {
1348                                         // still inside pusher, so it's really blocked
1349
1350                                         // fail the move
1351                                         if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1352                                                 continue;
1353                                         if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1354                                         {
1355                                                 // corpse
1356                                                 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1357                                                 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1358                                                 continue;
1359                                         }
1360
1361                                         VectorCopy (pushorig, pusher->fields.server->origin);
1362                                         VectorCopy (pushang, pusher->fields.server->angles);
1363                                         pusher->fields.server->ltime = pushltime;
1364                                         SV_LinkEdict (pusher, false);
1365
1366                                         // move back any entities we already moved
1367                                         for (i = 0;i < num_moved;i++)
1368                                         {
1369                                                 prvm_edict_t *ed = sv.moved_edicts[i];
1370                                                 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1371                                                 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1372                                                 SV_LinkEdict (ed, false);
1373                                         }
1374
1375                                         // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1376                                         if (pusher->fields.server->blocked)
1377                                         {
1378                                                 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1379                                                 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1380                                                 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1381                                         }
1382                                         break;
1383                                 }
1384                         }
1385                 }
1386         }
1387         pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1388         pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1389         pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1390 }
1391
1392 /*
1393 ================
1394 SV_Physics_Pusher
1395
1396 ================
1397 */
1398 void SV_Physics_Pusher (prvm_edict_t *ent)
1399 {
1400         float thinktime, oldltime, movetime;
1401
1402         oldltime = ent->fields.server->ltime;
1403
1404         thinktime = ent->fields.server->nextthink;
1405         if (thinktime < ent->fields.server->ltime + sv.frametime)
1406         {
1407                 movetime = thinktime - ent->fields.server->ltime;
1408                 if (movetime < 0)
1409                         movetime = 0;
1410         }
1411         else
1412                 movetime = sv.frametime;
1413
1414         if (movetime)
1415                 // advances ent->fields.server->ltime if not blocked
1416                 SV_PushMove (ent, movetime);
1417
1418         if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1419         {
1420                 ent->fields.server->nextthink = 0;
1421                 prog->globals.server->time = sv.time;
1422                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1423                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1424                 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1425         }
1426 }
1427
1428
1429 /*
1430 ===============================================================================
1431
1432 CLIENT MOVEMENT
1433
1434 ===============================================================================
1435 */
1436
1437 /*
1438 =============
1439 SV_CheckStuck
1440
1441 This is a big hack to try and fix the rare case of getting stuck in the world
1442 clipping hull.
1443 =============
1444 */
1445 void SV_CheckStuck (prvm_edict_t *ent)
1446 {
1447         int i, j, z;
1448         vec3_t org;
1449
1450         if (!SV_TestEntityPosition(ent))
1451         {
1452                 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1453                 return;
1454         }
1455
1456         VectorCopy (ent->fields.server->origin, org);
1457
1458         for (z=-1 ; z< 18 ; z++)
1459                 for (i=-1 ; i <= 1 ; i++)
1460                         for (j=-1 ; j <= 1 ; j++)
1461                         {
1462                                 ent->fields.server->origin[0] = org[0] + i;
1463                                 ent->fields.server->origin[1] = org[1] + j;
1464                                 ent->fields.server->origin[2] = org[2] + z;
1465                                 if (!SV_TestEntityPosition(ent))
1466                                 {
1467                                         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);
1468                                         SV_LinkEdict (ent, true);
1469                                         return;
1470                                 }
1471                         }
1472
1473         VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
1474         if (!SV_TestEntityPosition(ent))
1475         {
1476                 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1477                 SV_LinkEdict (ent, true);
1478                 return;
1479         }
1480
1481         VectorCopy (org, ent->fields.server->origin);
1482         Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1483 }
1484
1485 static void SV_UnstickEntity (prvm_edict_t *ent)
1486 {
1487         int i, j, z;
1488         vec3_t org;
1489
1490         // if not stuck in a bmodel, just return
1491         if (!SV_TestEntityPosition(ent))
1492                 return;
1493
1494         VectorCopy (ent->fields.server->origin, org);
1495
1496         for (z=-1 ; z< 18 ; z += 6)
1497                 for (i=-1 ; i <= 1 ; i++)
1498                         for (j=-1 ; j <= 1 ; j++)
1499                         {
1500                                 ent->fields.server->origin[0] = org[0] + i;
1501                                 ent->fields.server->origin[1] = org[1] + j;
1502                                 ent->fields.server->origin[2] = org[2] + z;
1503                                 if (!SV_TestEntityPosition(ent))
1504                                 {
1505                                         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);
1506                                         SV_LinkEdict (ent, true);
1507                                         return;
1508                                 }
1509                         }
1510
1511         VectorCopy (org, ent->fields.server->origin);
1512         if (developer.integer >= 100)
1513                 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1514 }
1515
1516
1517 /*
1518 =============
1519 SV_CheckWater
1520 =============
1521 */
1522 qboolean SV_CheckWater (prvm_edict_t *ent)
1523 {
1524         int cont;
1525         int nNativeContents;
1526         vec3_t point;
1527
1528         point[0] = ent->fields.server->origin[0];
1529         point[1] = ent->fields.server->origin[1];
1530         point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1531
1532         // DRESK - Support for Entity Contents Transition Event
1533         // NOTE: Some logic needed to be slightly re-ordered
1534         // to not affect performance and allow for the feature.
1535
1536         // Acquire Super Contents Prior to Resets
1537         cont = SV_PointSuperContents(point);
1538         // Acquire Native Contents Here
1539         nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1540
1541         // DRESK - Support for Entity Contents Transition Event
1542         if(ent->fields.server->watertype)
1543                 // Entity did NOT Spawn; Check
1544                 SV_CheckContentsTransition(ent, nNativeContents);
1545
1546
1547         ent->fields.server->waterlevel = 0;
1548         ent->fields.server->watertype = CONTENTS_EMPTY;
1549         cont = SV_PointSuperContents(point);
1550         if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1551         {
1552                 ent->fields.server->watertype = nNativeContents;
1553                 ent->fields.server->waterlevel = 1;
1554                 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1555                 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1556                 {
1557                         ent->fields.server->waterlevel = 2;
1558                         point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1559                         if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1560                                 ent->fields.server->waterlevel = 3;
1561                 }
1562         }
1563
1564         return ent->fields.server->waterlevel > 1;
1565 }
1566
1567 /*
1568 ============
1569 SV_WallFriction
1570
1571 ============
1572 */
1573 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1574 {
1575         float d, i;
1576         vec3_t forward, into, side;
1577
1578         AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1579         if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1580         {
1581                 // cut the tangential velocity
1582                 i = DotProduct (stepnormal, ent->fields.server->velocity);
1583                 VectorScale (stepnormal, i, into);
1584                 VectorSubtract (ent->fields.server->velocity, into, side);
1585                 ent->fields.server->velocity[0] = side[0] * (1 + d);
1586                 ent->fields.server->velocity[1] = side[1] * (1 + d);
1587         }
1588 }
1589
1590 #if 0
1591 /*
1592 =====================
1593 SV_TryUnstick
1594
1595 Player has come to a dead stop, possibly due to the problem with limited
1596 float precision at some angle joins in the BSP hull.
1597
1598 Try fixing by pushing one pixel in each direction.
1599
1600 This is a hack, but in the interest of good gameplay...
1601 ======================
1602 */
1603 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1604 {
1605         int i, clip;
1606         vec3_t oldorg, dir;
1607
1608         VectorCopy (ent->fields.server->origin, oldorg);
1609         VectorClear (dir);
1610
1611         for (i=0 ; i<8 ; i++)
1612         {
1613                 // try pushing a little in an axial direction
1614                 switch (i)
1615                 {
1616                         case 0: dir[0] = 2; dir[1] = 0; break;
1617                         case 1: dir[0] = 0; dir[1] = 2; break;
1618                         case 2: dir[0] = -2; dir[1] = 0; break;
1619                         case 3: dir[0] = 0; dir[1] = -2; break;
1620                         case 4: dir[0] = 2; dir[1] = 2; break;
1621                         case 5: dir[0] = -2; dir[1] = 2; break;
1622                         case 6: dir[0] = 2; dir[1] = -2; break;
1623                         case 7: dir[0] = -2; dir[1] = -2; break;
1624                 }
1625
1626                 SV_PushEntity (ent, dir, false);
1627
1628                 // retry the original move
1629                 ent->fields.server->velocity[0] = oldvel[0];
1630                 ent->fields.server->velocity[1] = oldvel[1];
1631                 ent->fields.server->velocity[2] = 0;
1632                 clip = SV_FlyMove (ent, 0.1, NULL);
1633
1634                 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1635                  || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1636                 {
1637                         Con_DPrint("TryUnstick - success.\n");
1638                         return clip;
1639                 }
1640
1641                 // go back to the original pos and try again
1642                 VectorCopy (oldorg, ent->fields.server->origin);
1643         }
1644
1645         // still not moving
1646         VectorClear (ent->fields.server->velocity);
1647         Con_DPrint("TryUnstick - failure.\n");
1648         return 7;
1649 }
1650 #endif
1651
1652 /*
1653 =====================
1654 SV_WalkMove
1655
1656 Only used by players
1657 ======================
1658 */
1659 void SV_WalkMove (prvm_edict_t *ent)
1660 {
1661         int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1662         vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1663         trace_t downtrace;
1664
1665         // if frametime is 0 (due to client sending the same timestamp twice),
1666         // don't move
1667         if (sv.frametime <= 0)
1668                 return;
1669
1670         SV_CheckVelocity(ent);
1671
1672         // do a regular slide move unless it looks like you ran into a step
1673         oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1674         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1675
1676         VectorCopy (ent->fields.server->origin, start_origin);
1677         VectorCopy (ent->fields.server->velocity, start_velocity);
1678
1679         clip = SV_FlyMove (ent, sv.frametime, NULL);
1680
1681         SV_CheckVelocity(ent);
1682
1683         VectorCopy(ent->fields.server->origin, originalmove_origin);
1684         VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1685         originalmove_clip = clip;
1686         originalmove_flags = (int)ent->fields.server->flags;
1687         originalmove_groundentity = ent->fields.server->groundentity;
1688
1689         if ((int)ent->fields.server->flags & FL_WATERJUMP)
1690                 return;
1691
1692         if (sv_nostep.integer)
1693                 return;
1694
1695         // if move didn't block on a step, return
1696         if (clip & 2)
1697         {
1698                 // if move was not trying to move into the step, return
1699                 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1700                         return;
1701
1702                 if (ent->fields.server->movetype != MOVETYPE_FLY)
1703                 {
1704                         // return if gibbed by a trigger
1705                         if (ent->fields.server->movetype != MOVETYPE_WALK)
1706                                 return;
1707
1708                         // only step up while jumping if that is enabled
1709                         if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1710                                 if (!oldonground && ent->fields.server->waterlevel == 0)
1711                                         return;
1712                 }
1713
1714                 // try moving up and forward to go up a step
1715                 // back to start pos
1716                 VectorCopy (start_origin, ent->fields.server->origin);
1717                 VectorCopy (start_velocity, ent->fields.server->velocity);
1718
1719                 // move up
1720                 VectorClear (upmove);
1721                 upmove[2] = sv_stepheight.value;
1722                 // FIXME: don't link?
1723                 SV_PushEntity(ent, upmove, false);
1724
1725                 // move forward
1726                 ent->fields.server->velocity[2] = 0;
1727                 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1728                 ent->fields.server->velocity[2] += start_velocity[2];
1729
1730                 SV_CheckVelocity(ent);
1731
1732                 // check for stuckness, possibly due to the limited precision of floats
1733                 // in the clipping hulls
1734                 if (clip
1735                  && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1736                  && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1737                 {
1738                         //Con_Printf("wall\n");
1739                         // stepping up didn't make any progress, revert to original move
1740                         VectorCopy(originalmove_origin, ent->fields.server->origin);
1741                         VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1742                         //clip = originalmove_clip;
1743                         ent->fields.server->flags = originalmove_flags;
1744                         ent->fields.server->groundentity = originalmove_groundentity;
1745                         // now try to unstick if needed
1746                         //clip = SV_TryUnstick (ent, oldvel);
1747                         return;
1748                 }
1749
1750                 //Con_Printf("step - ");
1751
1752                 // extra friction based on view angle
1753                 if (clip & 2 && sv_wallfriction.integer)
1754                         SV_WallFriction (ent, stepnormal);
1755         }
1756         // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1757         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)))
1758                 return;
1759
1760         // move down
1761         VectorClear (downmove);
1762         downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1763         // FIXME: don't link?
1764         downtrace = SV_PushEntity (ent, downmove, false);
1765
1766         if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1767         {
1768                 // this has been disabled so that you can't jump when you are stepping
1769                 // up while already jumping (also known as the Quake2 stair jump bug)
1770 #if 0
1771                 // LordHavoc: disabled this check so you can walk on monsters/players
1772                 //if (ent->fields.server->solid == SOLID_BSP)
1773                 {
1774                         //Con_Printf("onground\n");
1775                         ent->fields.server->flags =     (int)ent->fields.server->flags | FL_ONGROUND;
1776                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1777                 }
1778 #endif
1779         }
1780         else
1781         {
1782                 //Con_Printf("slope\n");
1783                 // if the push down didn't end up on good ground, use the move without
1784                 // the step up.  This happens near wall / slope combinations, and can
1785                 // cause the player to hop up higher on a slope too steep to climb
1786                 VectorCopy(originalmove_origin, ent->fields.server->origin);
1787                 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1788                 //clip = originalmove_clip;
1789                 ent->fields.server->flags = originalmove_flags;
1790                 ent->fields.server->groundentity = originalmove_groundentity;
1791         }
1792
1793         SV_CheckVelocity(ent);
1794 }
1795
1796 //============================================================================
1797
1798 /*
1799 =============
1800 SV_Physics_Follow
1801
1802 Entities that are "stuck" to another entity
1803 =============
1804 */
1805 void SV_Physics_Follow (prvm_edict_t *ent)
1806 {
1807         vec3_t vf, vr, vu, angles, v;
1808         prvm_edict_t *e;
1809
1810         // regular thinking
1811         if (!SV_RunThink (ent))
1812                 return;
1813
1814         // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1815         e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1816         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])
1817         {
1818                 // quick case for no rotation
1819                 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1820         }
1821         else
1822         {
1823                 angles[0] = -ent->fields.server->punchangle[0];
1824                 angles[1] =  ent->fields.server->punchangle[1];
1825                 angles[2] =  ent->fields.server->punchangle[2];
1826                 AngleVectors (angles, vf, vr, vu);
1827                 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];
1828                 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];
1829                 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];
1830                 angles[0] = -e->fields.server->angles[0];
1831                 angles[1] =  e->fields.server->angles[1];
1832                 angles[2] =  e->fields.server->angles[2];
1833                 AngleVectors (angles, vf, vr, vu);
1834                 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1835                 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1836                 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1837         }
1838         VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1839         SV_LinkEdict (ent, true);
1840 }
1841
1842 /*
1843 ==============================================================================
1844
1845 TOSS / BOUNCE
1846
1847 ==============================================================================
1848 */
1849
1850 /*
1851 =============
1852 SV_CheckWaterTransition
1853
1854 =============
1855 */
1856 void SV_CheckWaterTransition (prvm_edict_t *ent)
1857 {
1858         int cont;
1859         cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1860         if (!ent->fields.server->watertype)
1861         {
1862                 // just spawned here
1863                 ent->fields.server->watertype = cont;
1864                 ent->fields.server->waterlevel = 1;
1865                 return;
1866         }
1867
1868         // DRESK - Support for Entity Contents Transition Event
1869         // NOTE: Call here BEFORE updating the watertype below,
1870         // and suppress watersplash sound if a valid function
1871         // call was made to allow for custom "splash" sounds.
1872         if( !SV_CheckContentsTransition(ent, cont) )
1873         { // Contents Transition Function Invalid; Potentially Play Water Sound
1874                 // check if the entity crossed into or out of water
1875                 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1876                         SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1877         }
1878
1879         if (cont <= CONTENTS_WATER)
1880         {
1881                 ent->fields.server->watertype = cont;
1882                 ent->fields.server->waterlevel = 1;
1883         }
1884         else
1885         {
1886                 ent->fields.server->watertype = CONTENTS_EMPTY;
1887                 ent->fields.server->waterlevel = 0;
1888         }
1889 }
1890
1891 /*
1892 =============
1893 SV_Physics_Toss
1894
1895 Toss, bounce, and fly movement.  When onground, do nothing.
1896 =============
1897 */
1898 void SV_Physics_Toss (prvm_edict_t *ent)
1899 {
1900         trace_t trace;
1901         vec3_t move;
1902
1903 // if onground, return without moving
1904         if ((int)ent->fields.server->flags & FL_ONGROUND)
1905         {
1906                 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1907                 {
1908                         // don't stick to ground if onground and moving upward
1909                         ent->fields.server->flags -= FL_ONGROUND;
1910                 }
1911                 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1912                 {
1913                         // we can trust FL_ONGROUND if groundentity is world because it never moves
1914                         return;
1915                 }
1916                 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1917                 {
1918                         // if ent was supported by a brush model on previous frame,
1919                         // and groundentity is now freed, set groundentity to 0 (world)
1920                         // which leaves it suspended in the air
1921                         ent->fields.server->groundentity = 0;
1922                         return;
1923                 }
1924         }
1925         ent->priv.server->suspendedinairflag = false;
1926
1927         SV_CheckVelocity (ent);
1928
1929 // add gravity
1930         if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1931                 SV_AddGravity (ent);
1932
1933 // move angles
1934         VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1935
1936 // move origin
1937         VectorScale (ent->fields.server->velocity, sv.frametime, move);
1938         trace = SV_PushEntity (ent, move, true);
1939         if (ent->priv.server->free)
1940                 return;
1941         if (trace.bmodelstartsolid)
1942         {
1943                 // try to unstick the entity
1944                 SV_UnstickEntity(ent);
1945                 trace = SV_PushEntity (ent, move, false);
1946                 if (ent->priv.server->free)
1947                         return;
1948         }
1949
1950         if (trace.fraction < 1)
1951         {
1952                 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1953                 {
1954                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1955                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1956                 }
1957                 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1958                 {
1959                         float d;
1960                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1961                         // LordHavoc: fixed grenades not bouncing when fired down a slope
1962                         if (sv_gameplayfix_grenadebouncedownslopes.integer)
1963                         {
1964                                 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1965                                 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1966                                 {
1967                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1968                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1969                                         VectorClear (ent->fields.server->velocity);
1970                                         VectorClear (ent->fields.server->avelocity);
1971                                 }
1972                                 else
1973                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1974                         }
1975                         else
1976                         {
1977                                 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1978                                 {
1979                                         ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1980                                         ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1981                                         VectorClear (ent->fields.server->velocity);
1982                                         VectorClear (ent->fields.server->avelocity);
1983                                 }
1984                                 else
1985                                         ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1986                         }
1987                 }
1988                 else
1989                 {
1990                         ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1991                         if (trace.plane.normal[2] > 0.7)
1992                         {
1993                                 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1994                                 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1995                                 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1996                                         ent->priv.server->suspendedinairflag = true;
1997                                 VectorClear (ent->fields.server->velocity);
1998                                 VectorClear (ent->fields.server->avelocity);
1999                         }
2000                         else
2001                                 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2002                 }
2003         }
2004
2005 // check for in water
2006         SV_CheckWaterTransition (ent);
2007 }
2008
2009 /*
2010 ===============================================================================
2011
2012 STEPPING MOVEMENT
2013
2014 ===============================================================================
2015 */
2016
2017 /*
2018 =============
2019 SV_Physics_Step
2020
2021 Monsters freefall when they don't have a ground entity, otherwise
2022 all movement is done with discrete steps.
2023
2024 This is also used for objects that have become still on the ground, but
2025 will fall if the floor is pulled out from under them.
2026 =============
2027 */
2028 void SV_Physics_Step (prvm_edict_t *ent)
2029 {
2030         int flags = (int)ent->fields.server->flags;
2031         // don't fall at all if fly/swim
2032         if (!(flags & (FL_FLY | FL_SWIM)))
2033         {
2034                 if (flags & FL_ONGROUND)
2035                 {
2036                         // freefall if onground and moving upward
2037                         // freefall if not standing on a world surface (it may be a lift or trap door)
2038                         if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2039                         {
2040                                 ent->fields.server->flags -= FL_ONGROUND;
2041                                 SV_AddGravity(ent);
2042                                 SV_CheckVelocity(ent);
2043                                 SV_FlyMove(ent, sv.frametime, NULL);
2044                                 SV_LinkEdict(ent, true);
2045                         }
2046                 }
2047                 else
2048                 {
2049                         // freefall if not onground
2050                         int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2051
2052                         SV_AddGravity(ent);
2053                         SV_CheckVelocity(ent);
2054                         SV_FlyMove(ent, sv.frametime, NULL);
2055                         SV_LinkEdict(ent, true);
2056
2057                         // just hit ground
2058                         if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
2059                                 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2060                 }
2061         }
2062
2063 // regular thinking
2064         SV_RunThink(ent);
2065
2066         SV_CheckWaterTransition(ent);
2067 }
2068
2069 //============================================================================
2070
2071 static void SV_Physics_Entity (prvm_edict_t *ent)
2072 {
2073         // don't run a move on newly spawned projectiles as it messes up movement
2074         // interpolation and rocket trails
2075         qboolean runmove = ent->priv.server->move;
2076         ent->priv.server->move = true;
2077         switch ((int) ent->fields.server->movetype)
2078         {
2079         case MOVETYPE_PUSH:
2080         case MOVETYPE_FAKEPUSH:
2081                 SV_Physics_Pusher (ent);
2082                 break;
2083         case MOVETYPE_NONE:
2084                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2085                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2086                         SV_RunThink (ent);
2087                 break;
2088         case MOVETYPE_FOLLOW:
2089                 SV_Physics_Follow (ent);
2090                 break;
2091         case MOVETYPE_NOCLIP:
2092                 if (SV_RunThink(ent))
2093                 {
2094                         SV_CheckWater(ent);
2095                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2096                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2097                 }
2098                 SV_LinkEdict(ent, false);
2099                 break;
2100         case MOVETYPE_STEP:
2101                 SV_Physics_Step (ent);
2102                 break;
2103         case MOVETYPE_WALK:
2104                 if (SV_RunThink (ent))
2105                 {
2106                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2107                                 SV_AddGravity (ent);
2108                         SV_CheckStuck (ent);
2109                         SV_WalkMove (ent);
2110                         SV_LinkEdict (ent, true);
2111                 }
2112                 break;
2113         case MOVETYPE_TOSS:
2114         case MOVETYPE_BOUNCE:
2115         case MOVETYPE_BOUNCEMISSILE:
2116         case MOVETYPE_FLYMISSILE:
2117         case MOVETYPE_FLY:
2118                 // regular thinking
2119                 if (SV_RunThink (ent) && runmove)
2120                         SV_Physics_Toss (ent);
2121                 break;
2122         default:
2123                 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2124                 break;
2125         }
2126 }
2127
2128 void SV_Physics_ClientEntity (prvm_edict_t *ent)
2129 {
2130         SV_ApplyClientMove();
2131         // make sure the velocity is sane (not a NaN)
2132         SV_CheckVelocity(ent);
2133         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
2134         if (prog->funcoffsets.SV_PlayerPhysics && sv_playerphysicsqc.integer)
2135         {
2136                 prog->globals.server->time = sv.time;
2137                 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2138                 PRVM_ExecuteProgram (prog->funcoffsets.SV_PlayerPhysics, "QC function SV_PlayerPhysics is missing");
2139         }
2140         else
2141                 SV_ClientThink ();
2142         // make sure the velocity is sane (not a NaN)
2143         SV_CheckVelocity(ent);
2144         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2145         // player_run/player_stand1 does not horribly malfunction if the
2146         // velocity becomes a number that is both == 0 and != 0
2147         // (sounds to me like NaN but to be absolutely safe...)
2148         if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2149                 VectorClear(ent->fields.server->velocity);
2150         // call standard client pre-think
2151         prog->globals.server->time = sv.time;
2152         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2153         PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2154         SV_CheckVelocity (ent);
2155
2156         switch ((int) ent->fields.server->movetype)
2157         {
2158         case MOVETYPE_PUSH:
2159         case MOVETYPE_FAKEPUSH:
2160                 SV_Physics_Pusher (ent);
2161                 break;
2162         case MOVETYPE_NONE:
2163                 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2164                 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2165                         SV_RunThink (ent);
2166                 break;
2167         case MOVETYPE_FOLLOW:
2168                 SV_Physics_Follow (ent);
2169                 break;
2170         case MOVETYPE_NOCLIP:
2171                 if (SV_RunThink(ent))
2172                 {
2173                         SV_CheckWater(ent);
2174                         VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2175                         VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2176                 }
2177                 break;
2178         case MOVETYPE_STEP:
2179                 SV_Physics_Step (ent);
2180                 break;
2181         case MOVETYPE_WALK:
2182                 if (SV_RunThink (ent))
2183                 {
2184                         if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2185                                 SV_AddGravity (ent);
2186                         SV_CheckStuck (ent);
2187                         SV_WalkMove (ent);
2188                 }
2189                 break;
2190         case MOVETYPE_TOSS:
2191         case MOVETYPE_BOUNCE:
2192         case MOVETYPE_BOUNCEMISSILE:
2193         case MOVETYPE_FLYMISSILE:
2194                 // regular thinking
2195                 if (SV_RunThink (ent))
2196                         SV_Physics_Toss (ent);
2197                 break;
2198         case MOVETYPE_FLY:
2199                 if (SV_RunThink (ent))
2200                 {
2201                         SV_CheckWater (ent);
2202                         SV_WalkMove (ent);
2203                 }
2204                 break;
2205         default:
2206                 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2207                 break;
2208         }
2209
2210         SV_CheckVelocity (ent);
2211
2212         // call standard player post-think
2213         SV_LinkEdict (ent, true);
2214
2215         SV_CheckVelocity (ent);
2216
2217         prog->globals.server->time = sv.time;
2218         prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2219         PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2220 }
2221
2222 /*
2223 ================
2224 SV_Physics
2225
2226 ================
2227 */
2228 void SV_Physics (void)
2229 {
2230         int i;
2231         prvm_edict_t *ent;
2232
2233 // let the progs know that a new frame has started
2234         prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2235         prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2236         prog->globals.server->time = sv.time;
2237         prog->globals.server->frametime = sv.frametime;
2238         PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2239
2240 //
2241 // treat each object in turn
2242 //
2243
2244         // if force_retouch, relink all the entities
2245         if (prog->globals.server->force_retouch > 0)
2246                 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2247                         if (!ent->priv.server->free)
2248                                 SV_LinkEdict (ent, true);       // force retouch even for stationary
2249
2250         // run physics on the client entities
2251         for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2252         {
2253                 if (!ent->priv.server->free)
2254                 {
2255                         // don't do physics on disconnected clients, FrikBot relies on this
2256                         if (!host_client->spawned)
2257                                 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2258                         // don't run physics here if running asynchronously
2259                         else if (host_client->clmovement_skipphysicsframes > 0)
2260                                 host_client->clmovement_skipphysicsframes--;
2261                         else
2262                                 SV_Physics_ClientEntity(ent);
2263                 }
2264         }
2265
2266         // run physics on all the non-client entities
2267         if (!sv_freezenonclients.integer)
2268                 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2269                         if (!ent->priv.server->free)
2270                                 SV_Physics_Entity(ent);
2271
2272         if (prog->globals.server->force_retouch > 0)
2273                 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2274
2275         // LordHavoc: endframe support
2276         if (prog->funcoffsets.EndFrame)
2277         {
2278                 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2279                 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2280                 prog->globals.server->time = sv.time;
2281                 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2282         }
2283
2284         // decrement prog->num_edicts if the highest number entities died
2285         for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2286
2287         if (!sv_freezenonclients.integer)
2288                 sv.time += sv.frametime;
2289 }
2290
2291
2292 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
2293 {
2294         int i;
2295         float gravity;
2296         vec3_t move, end;
2297         vec3_t original_origin;
2298         vec3_t original_velocity;
2299         vec3_t original_angles;
2300         vec3_t original_avelocity;
2301         prvm_eval_t *val;
2302         trace_t trace;
2303
2304         VectorCopy(tossent->fields.server->origin   , original_origin   );
2305         VectorCopy(tossent->fields.server->velocity , original_velocity );
2306         VectorCopy(tossent->fields.server->angles   , original_angles   );
2307         VectorCopy(tossent->fields.server->avelocity, original_avelocity);
2308
2309         val = PRVM_EDICTFIELDVALUE(tossent, prog->fieldoffsets.gravity);
2310         if (val != NULL && val->_float != 0)
2311                 gravity = val->_float;
2312         else
2313                 gravity = 1.0;
2314         gravity *= sv_gravity.value * 0.05;
2315
2316         for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
2317         {
2318                 SV_CheckVelocity (tossent);
2319                 tossent->fields.server->velocity[2] -= gravity;
2320                 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
2321                 VectorScale (tossent->fields.server->velocity, 0.05, move);
2322                 VectorAdd (tossent->fields.server->origin, move, end);
2323                 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
2324                 VectorCopy (trace.endpos, tossent->fields.server->origin);
2325
2326                 if (trace.fraction < 1)
2327                         break;
2328         }
2329
2330         VectorCopy(original_origin   , tossent->fields.server->origin   );
2331         VectorCopy(original_velocity , tossent->fields.server->velocity );
2332         VectorCopy(original_angles   , tossent->fields.server->angles   );
2333         VectorCopy(original_avelocity, tossent->fields.server->avelocity);
2334
2335         return trace;
2336 }
2337