2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
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.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
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
38 solid_edge items only clip against bsp models.
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"};
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)"};
59 // TODO: move this extern to server.h
60 extern cvar_t sv_clmovement_waitforinput;
62 #define MOVE_EPSILON 0.01
64 void SV_Physics_Toss (prvm_edict_t *ent);
66 void SV_Phys_Init (void)
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);
76 Cvar_RegisterVariable(&sv_sound_watersplash);
77 Cvar_RegisterVariable(&sv_sound_land);
81 ===============================================================================
85 ===============================================================================
88 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
93 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
94 if (val && val->_float)
95 return (int)val->_float;
96 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
98 if ((int)passedict->fields.server->flags & FL_MONSTER)
99 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
101 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
103 else if (passedict->fields.server->solid == SOLID_CORPSE)
104 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
105 else if (passedict->fields.server->solid == SOLID_TRIGGER)
106 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
108 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
111 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
119 #if COLLISIONPARANOID >= 1
120 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, int hitsupercontentsmask)
122 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, int hitsupercontentsmask)
125 vec3_t hullmins, hullmaxs;
126 int i, bodysupercontents;
129 prvm_edict_t *traceowner, *touch;
131 // bounding box of entire move area
132 vec3_t clipboxmins, clipboxmaxs;
133 // size of the moving object
134 vec3_t clipmins, clipmaxs;
135 // size when clipping against monsters
136 vec3_t clipmins2, clipmaxs2;
137 // start and end origin of move
138 vec3_t clipstart, clipend;
141 // matrices to transform into/out of other entity's space
142 matrix4x4_t matrix, imatrix;
143 // model of other entity
145 // list of entities to test for collisions
147 prvm_edict_t *touchedicts[MAX_EDICTS];
149 VectorCopy(start, clipstart);
150 VectorCopy(end, clipend);
151 VectorCopy(mins, clipmins);
152 VectorCopy(maxs, clipmaxs);
153 VectorCopy(mins, clipmins2);
154 VectorCopy(maxs, clipmaxs2);
155 #if COLLISIONPARANOID >= 3
156 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
160 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
161 cliptrace.bmodelstartsolid = cliptrace.startsolid;
162 if (cliptrace.startsolid || cliptrace.fraction < 1)
163 cliptrace.ent = prog->edicts;
164 if (type == MOVE_WORLDONLY)
167 if (type == MOVE_MISSILE)
169 // LordHavoc: modified this, was = -15, now -= 15
170 for (i = 0;i < 3;i++)
177 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
178 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
179 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
182 VectorCopy(clipmins, hullmins);
183 VectorCopy(clipmaxs, hullmaxs);
186 // create the bounding box of the entire move
187 for (i = 0;i < 3;i++)
189 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
190 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
193 // debug override to test against everything
194 if (sv_debugmove.integer)
196 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
197 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
200 // if the passedict is world, make it NULL (to avoid two checks each time)
201 if (passedict == prog->edicts)
203 // precalculate prog value for passedict for comparisons
204 passedictprog = PRVM_EDICT_TO_PROG(passedict);
205 // figure out whether this is a point trace for comparisons
206 pointtrace = VectorCompare(clipmins, clipmaxs);
207 // precalculate passedict's owner edict pointer for comparisons
208 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
211 // because this uses World_EntitiestoBox, we know all entity boxes overlap
212 // the clip region, so we can skip culling checks in the loop below
213 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
214 if (numtouchedicts > MAX_EDICTS)
216 // this never happens
217 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
218 numtouchedicts = MAX_EDICTS;
220 for (i = 0;i < numtouchedicts;i++)
222 touch = touchedicts[i];
224 if (touch->fields.server->solid < SOLID_BBOX)
226 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
231 // don't clip against self
232 if (passedict == touch)
234 // don't clip owned entities against owner
235 if (traceowner == touch)
237 // don't clip owner against owned entities
238 if (passedictprog == touch->fields.server->owner)
240 // don't clip points against points (they can't collide)
241 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
245 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
247 // might interact, so do an exact clip
249 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
251 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
252 // if the modelindex is 0, it shouldn't be SOLID_BSP!
253 if (modelindex > 0 && modelindex < MAX_MODELS)
254 model = sv.models[(int)touch->fields.server->modelindex];
257 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
259 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
260 Matrix4x4_Invert_Simple(&imatrix, &matrix);
261 if ((int)touch->fields.server->flags & FL_MONSTER)
262 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
264 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
266 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
272 #if COLLISIONPARANOID >= 1
273 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, int hitsupercontentsmask)
278 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
281 VectorCopy(trace.endpos, temp);
282 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
283 #if COLLISIONPARANOID < 3
284 if (trace.startsolid || endstuck)
286 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" : "");
293 ===============================================================================
295 Linking entities into the world culling system
297 ===============================================================================
300 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
302 int i, numtouchedicts, old_self, old_other;
303 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
305 // build a list of edicts to touch, because the link loop can be corrupted
306 // by SV_IncreaseEdicts called during touch functions
307 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
308 if (numtouchedicts > MAX_EDICTS)
310 // this never happens
311 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
312 numtouchedicts = MAX_EDICTS;
315 old_self = prog->globals.server->self;
316 old_other = prog->globals.server->other;
317 for (i = 0;i < numtouchedicts;i++)
319 touch = touchedicts[i];
320 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
323 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
324 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
325 prog->globals.server->time = sv.time;
326 prog->globals.server->trace_allsolid = false;
327 prog->globals.server->trace_startsolid = false;
328 prog->globals.server->trace_fraction = 1;
329 prog->globals.server->trace_inwater = false;
330 prog->globals.server->trace_inopen = true;
331 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
332 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
333 prog->globals.server->trace_plane_dist = 0;
334 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
335 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
337 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
339 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
341 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
343 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
346 prog->globals.server->self = old_self;
347 prog->globals.server->other = old_other;
356 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
361 if (ent == prog->edicts)
362 return; // don't add the world
364 if (ent->priv.server->free)
369 if (ent->fields.server->solid == SOLID_BSP)
371 int modelindex = (int)ent->fields.server->modelindex;
372 if (modelindex < 0 || modelindex > MAX_MODELS)
374 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
377 model = sv.models[modelindex];
380 if (!model->TraceBox)
381 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
383 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
385 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
386 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
388 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
390 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
391 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
395 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
396 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
401 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
402 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
403 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
408 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
409 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
413 // to make items easier to pick up and allow them to be grabbed off
414 // of shelves, the abs sizes are expanded
416 if ((int)ent->fields.server->flags & FL_ITEM)
427 // because movement is clipped an epsilon away from an actual edge,
428 // we must fully check even when bounding boxes don't quite touch
437 VectorCopy(mins, ent->fields.server->absmin);
438 VectorCopy(maxs, ent->fields.server->absmax);
440 World_LinkEdict(&sv.world, ent, mins, maxs);
442 // if touch_triggers, call touch on all entities overlapping this box
443 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
444 SV_LinkEdict_TouchAreaGrid(ent);
448 ===============================================================================
452 ===============================================================================
457 SV_TestEntityPosition
459 returns true if the entity is in solid currently
462 static int SV_TestEntityPosition (prvm_edict_t *ent)
464 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
465 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
469 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
471 // q1bsp/hlbsp use hulls and if the entity does not exactly match
472 // a hull size it is incorrectly tested, so this code tries to
473 // 'fix' it slightly...
476 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
477 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
478 VectorSubtract(m2, m1, s);
479 #define EPSILON (1.0f / 32.0f)
480 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
481 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
482 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
483 for (i = 0;i < 8;i++)
485 v[0] = (i & 1) ? m2[0] : m1[0];
486 v[1] = (i & 2) ? m2[1] : m1[1];
487 v[2] = (i & 4) ? m2[2] : m1[2];
488 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
501 void SV_CheckAllEnts (void)
506 // see if any solid entities are inside the final position
507 check = PRVM_NEXT_EDICT(prog->edicts);
508 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
510 if (check->priv.server->free)
512 if (check->fields.server->movetype == MOVETYPE_PUSH
513 || check->fields.server->movetype == MOVETYPE_NONE
514 || check->fields.server->movetype == MOVETYPE_FOLLOW
515 || check->fields.server->movetype == MOVETYPE_NOCLIP)
518 if (SV_TestEntityPosition (check))
519 Con_Print("entity in invalid position\n");
523 // DRESK - Support for Entity Contents Transition Event
526 SV_CheckContentsTransition
528 returns true if entity had a valid contentstransition function call
531 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
533 int bValidFunctionCall;
534 prvm_eval_t *contentstransition;
536 // Default Valid Function Call to False
537 bValidFunctionCall = false;
539 if(ent->fields.server->watertype != nContents)
540 { // Changed Contents
541 // Acquire Contents Transition Function from QC
542 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
544 if(contentstransition->function)
545 { // Valid Function; Execute
546 // Assign Valid Function
547 bValidFunctionCall = true;
548 // Prepare Parameters (Original Contents, New Contents)
550 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
552 PRVM_G_FLOAT(OFS_PARM1) = nContents;
553 // Execute VM Function
554 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
558 // Return if Function Call was Valid
559 return bValidFunctionCall;
568 void SV_CheckVelocity (prvm_edict_t *ent)
576 for (i=0 ; i<3 ; i++)
578 if (IS_NAN(ent->fields.server->velocity[i]))
580 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
581 ent->fields.server->velocity[i] = 0;
583 if (IS_NAN(ent->fields.server->origin[i]))
585 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
586 ent->fields.server->origin[i] = 0;
590 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
591 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
592 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
594 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
595 ent->fields.server->velocity[0] *= wishspeed;
596 ent->fields.server->velocity[1] *= wishspeed;
597 ent->fields.server->velocity[2] *= wishspeed;
605 Runs thinking code if time. There is some play in the exact time the think
606 function will be called, because it is called before any movement is done
607 in a frame. Not used for pushmove objects, because they must be exact.
608 Returns false if the entity removed itself.
611 qboolean SV_RunThink (prvm_edict_t *ent)
615 thinktime = ent->fields.server->nextthink;
616 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
619 // don't let things stay in the past.
620 // it is possible to start that way by a trigger with a local time.
621 if (thinktime < sv.time)
624 ent->fields.server->nextthink = 0;
625 prog->globals.server->time = thinktime;
626 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
627 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
628 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
629 return !ent->priv.server->free;
636 Two entities have touched, so run their touch functions
639 extern void VM_SetTraceGlobals(const trace_t *trace);
640 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
642 int old_self, old_other;
643 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
646 old_self = prog->globals.server->self;
647 old_other = prog->globals.server->other;
649 VM_SetTraceGlobals(trace);
651 prog->globals.server->time = sv.time;
652 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
654 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
655 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
656 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
659 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
661 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
662 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
663 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
664 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
665 prog->globals.server->trace_plane_dist = -trace->plane.dist;
666 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
667 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
669 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
671 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
673 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
675 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
678 prog->globals.server->self = old_self;
679 prog->globals.server->other = old_other;
687 Slide off of the impacting object
688 returns the blocked flags (1 = floor, 2 = step / wall)
691 #define STOP_EPSILON 0.1
692 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
697 backoff = -DotProduct (in, normal) * overbounce;
698 VectorMA(in, backoff, normal, out);
700 for (i = 0;i < 3;i++)
701 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
710 The basic solid body movement clip that slides along multiple planes
711 Returns the clipflags if the velocity was modified (hit something solid)
715 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
718 // LordHavoc: increased from 5 to 32
719 #define MAX_CLIP_PLANES 32
720 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
722 int blocked, bumpcount;
723 int i, j, impact, numplanes;
725 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
730 VectorCopy(ent->fields.server->velocity, original_velocity);
731 VectorCopy(ent->fields.server->velocity, primal_velocity);
734 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
736 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
739 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
740 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
742 //if (trace.fraction < 0.002)
747 VectorCopy(ent->fields.server->origin, start);
748 start[2] += 3;//0.03125;
749 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
750 end[2] += 3;//0.03125;
751 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
752 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)))
754 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
760 for (i = 0;i < numplanes;i++)
762 VectorCopy(ent->fields.server->origin, start);
763 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
764 VectorMA(start, 3, planes[i], start);
765 VectorMA(end, 3, planes[i], end);
766 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
767 if (trace.fraction < testtrace.fraction)
770 VectorCopy(start, ent->fields.server->origin);
775 // VectorAdd(ent->fields.server->origin, planes[j], start);
781 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);
782 if (trace.fraction < 1)
783 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
788 if (trace.bmodelstartsolid)
790 // LordHavoc: note: this code is what makes entities stick in place
791 // if embedded in world only (you can walk through other objects if
793 // entity is trapped in another solid
794 VectorClear(ent->fields.server->velocity);
799 // break if it moved the entire distance
800 if (trace.fraction == 1)
802 VectorCopy(trace.endpos, ent->fields.server->origin);
808 Con_Printf ("SV_FlyMove: !trace.ent");
809 trace.ent = prog->edicts;
812 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
814 if (trace.plane.normal[2])
816 if (trace.plane.normal[2] > 0.7)
820 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
821 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
828 // save the trace for player extrafriction
830 VectorCopy(trace.plane.normal, stepnormal);
833 if (trace.fraction >= 0.001)
835 // actually covered some distance
836 VectorCopy(trace.endpos, ent->fields.server->origin);
837 VectorCopy(ent->fields.server->velocity, original_velocity);
841 // run the impact function
844 SV_Impact(ent, &trace);
846 // break if removed by the impact function
847 if (ent->priv.server->free)
851 time_left *= 1 - trace.fraction;
853 // clipped to another plane
854 if (numplanes >= MAX_CLIP_PLANES)
856 // this shouldn't really happen
857 VectorClear(ent->fields.server->velocity);
863 for (i = 0;i < numplanes;i++)
864 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
868 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
873 VectorCopy(trace.plane.normal, planes[numplanes]);
876 if (sv_newflymove.integer)
877 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
880 // modify original_velocity so it parallels all of the clip planes
881 for (i = 0;i < numplanes;i++)
883 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
884 for (j = 0;j < numplanes;j++)
889 if (DotProduct(new_velocity, planes[j]) < 0)
899 // go along this plane
900 VectorCopy(new_velocity, ent->fields.server->velocity);
904 // go along the crease
907 VectorClear(ent->fields.server->velocity);
911 CrossProduct(planes[0], planes[1], dir);
912 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
913 VectorNormalize(dir);
914 d = DotProduct(dir, ent->fields.server->velocity);
915 VectorScale(dir, d, ent->fields.server->velocity);
919 // if current velocity is against the original velocity,
920 // stop dead to avoid tiny occilations in sloping corners
921 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
923 VectorClear(ent->fields.server->velocity);
928 //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]);
931 if ((blocked & 1) == 0 && bumpcount > 1)
933 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
934 // flag ONGROUND if there's ground under it
935 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
939 // LordHavoc: this came from QW and allows you to get out of water more easily
940 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
941 VectorCopy(primal_velocity, ent->fields.server->velocity);
951 void SV_AddGravity (prvm_edict_t *ent)
956 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
957 if (val!=0 && val->_float)
958 ent_gravity = val->_float;
961 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
966 ===============================================================================
970 ===============================================================================
977 Does not change the entities velocity at all
980 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
986 VectorAdd (ent->fields.server->origin, push, end);
988 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
990 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
991 type = MOVE_NOMONSTERS; // only clip against bmodels
995 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
996 if (trace.bmodelstartsolid && failonbmodelstartsolid)
999 VectorCopy (trace.endpos, ent->fields.server->origin);
1000 SV_LinkEdict (ent, true);
1002 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)))
1003 SV_Impact (ent, &trace);
1014 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1017 float savesolid, movetime2, pushltime;
1018 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1020 int numcheckentities;
1021 static prvm_edict_t *checkentities[MAX_EDICTS];
1022 model_t *pushermodel;
1024 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1026 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])
1028 pusher->fields.server->ltime += movetime;
1032 switch ((int) pusher->fields.server->solid)
1034 // LordHavoc: valid pusher types
1037 case SOLID_SLIDEBOX:
1038 case SOLID_CORPSE: // LordHavoc: this would be weird...
1040 // LordHavoc: no collisions
1043 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1044 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1045 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1046 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1047 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1048 pusher->fields.server->ltime += movetime;
1049 SV_LinkEdict (pusher, false);
1052 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1055 index = (int) pusher->fields.server->modelindex;
1056 if (index < 1 || index >= MAX_MODELS)
1058 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1061 pushermodel = sv.models[index];
1063 movetime2 = movetime;
1064 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1065 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1066 if (moveangle[0] || moveangle[2])
1068 for (i = 0;i < 3;i++)
1072 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1073 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1077 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1078 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1082 else if (moveangle[1])
1084 for (i = 0;i < 3;i++)
1088 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1089 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1093 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1094 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1100 for (i = 0;i < 3;i++)
1104 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1105 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1109 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1110 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1115 VectorNegate (moveangle, a);
1116 AngleVectorsFLU (a, forward, left, up);
1118 VectorCopy (pusher->fields.server->origin, pushorig);
1119 VectorCopy (pusher->fields.server->angles, pushang);
1120 pushltime = pusher->fields.server->ltime;
1122 // move the pusher to its final position
1124 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1125 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1126 pusher->fields.server->ltime += movetime;
1127 SV_LinkEdict (pusher, false);
1130 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1131 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1132 Matrix4x4_CreateFromQuakeEntity(&pusherfinalmatrix, pusher->fields.server->origin[0], pusher->fields.server->origin[1], pusher->fields.server->origin[2], pusher->fields.server->angles[0], pusher->fields.server->angles[1], pusher->fields.server->angles[2], 1);
1133 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1135 savesolid = pusher->fields.server->solid;
1137 // see if any solid entities are inside the final position
1140 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1141 for (e = 0;e < numcheckentities;e++)
1143 prvm_edict_t *check = checkentities[e];
1144 if (check->fields.server->movetype == MOVETYPE_NONE
1145 || check->fields.server->movetype == MOVETYPE_PUSH
1146 || check->fields.server->movetype == MOVETYPE_FOLLOW
1147 || check->fields.server->movetype == MOVETYPE_NOCLIP
1148 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1151 // if the entity is standing on the pusher, it will definitely be moved
1152 // if the entity is not standing on the pusher, but is in the pusher's
1153 // final position, move it
1154 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1156 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1157 if (!trace.startsolid)
1162 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1165 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1166 org2[0] = DotProduct (org, forward);
1167 org2[1] = DotProduct (org, left);
1168 org2[2] = DotProduct (org, up);
1169 VectorSubtract (org2, org, move);
1170 VectorAdd (move, move1, move);
1173 VectorCopy (move1, move);
1175 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1176 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1177 sv.moved_edicts[num_moved++] = check;
1179 // try moving the contacted entity
1180 pusher->fields.server->solid = SOLID_NOT;
1181 trace = SV_PushEntity (check, move, true);
1182 // FIXME: turn players specially
1183 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1184 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1185 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1187 // this check is for items riding platforms that are passing under (or
1188 // through) walls intended to knock the items off
1189 if (trace.fraction < 1 && check->fields.server->movetype != MOVETYPE_WALK)
1190 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1192 // if it is still inside the pusher, block
1193 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1194 if (trace.startsolid)
1196 // try moving the contacted entity a tiny bit further to account for precision errors
1198 pusher->fields.server->solid = SOLID_NOT;
1199 VectorScale(move, 1.1, move2);
1200 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1201 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1202 SV_PushEntity (check, move2, true);
1203 pusher->fields.server->solid = savesolid;
1204 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1205 if (trace.startsolid)
1207 // try moving the contacted entity a tiny bit less to account for precision errors
1208 pusher->fields.server->solid = SOLID_NOT;
1209 VectorScale(move, 0.9, move2);
1210 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1211 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1212 SV_PushEntity (check, move2, true);
1213 pusher->fields.server->solid = savesolid;
1214 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1215 if (trace.startsolid)
1217 // still inside pusher, so it's really blocked
1220 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1222 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1225 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1226 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1230 VectorCopy (pushorig, pusher->fields.server->origin);
1231 VectorCopy (pushang, pusher->fields.server->angles);
1232 pusher->fields.server->ltime = pushltime;
1233 SV_LinkEdict (pusher, false);
1235 // move back any entities we already moved
1236 for (i = 0;i < num_moved;i++)
1238 prvm_edict_t *ed = sv.moved_edicts[i];
1239 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1240 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1241 SV_LinkEdict (ed, false);
1244 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1245 if (pusher->fields.server->blocked)
1247 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1248 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1249 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1256 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1257 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1258 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1267 void SV_Physics_Pusher (prvm_edict_t *ent)
1269 float thinktime, oldltime, movetime;
1271 oldltime = ent->fields.server->ltime;
1273 thinktime = ent->fields.server->nextthink;
1274 if (thinktime < ent->fields.server->ltime + sv.frametime)
1276 movetime = thinktime - ent->fields.server->ltime;
1281 movetime = sv.frametime;
1284 // advances ent->fields.server->ltime if not blocked
1285 SV_PushMove (ent, movetime);
1287 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1289 ent->fields.server->nextthink = 0;
1290 prog->globals.server->time = sv.time;
1291 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1292 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1293 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1299 ===============================================================================
1303 ===============================================================================
1310 This is a big hack to try and fix the rare case of getting stuck in the world
1314 void SV_CheckStuck (prvm_edict_t *ent)
1319 if (!SV_TestEntityPosition(ent))
1321 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1325 VectorCopy (ent->fields.server->origin, org);
1327 for (z=-1 ; z< 18 ; z++)
1328 for (i=-1 ; i <= 1 ; i++)
1329 for (j=-1 ; j <= 1 ; j++)
1331 ent->fields.server->origin[0] = org[0] + i;
1332 ent->fields.server->origin[1] = org[1] + j;
1333 ent->fields.server->origin[2] = org[2] + z;
1334 if (!SV_TestEntityPosition(ent))
1336 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);
1337 SV_LinkEdict (ent, true);
1342 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
1343 if (!SV_TestEntityPosition(ent))
1345 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1346 SV_LinkEdict (ent, true);
1350 VectorCopy (org, ent->fields.server->origin);
1351 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1354 static void SV_UnstickEntity (prvm_edict_t *ent)
1359 // if not stuck in a bmodel, just return
1360 if (!SV_TestEntityPosition(ent))
1363 VectorCopy (ent->fields.server->origin, org);
1365 for (z=-1 ; z< 18 ; z += 6)
1366 for (i=-1 ; i <= 1 ; i++)
1367 for (j=-1 ; j <= 1 ; j++)
1369 ent->fields.server->origin[0] = org[0] + i;
1370 ent->fields.server->origin[1] = org[1] + j;
1371 ent->fields.server->origin[2] = org[2] + z;
1372 if (!SV_TestEntityPosition(ent))
1374 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);
1375 SV_LinkEdict (ent, true);
1380 VectorCopy (org, ent->fields.server->origin);
1381 if (developer.integer >= 100)
1382 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1391 qboolean SV_CheckWater (prvm_edict_t *ent)
1394 int nNativeContents;
1397 point[0] = ent->fields.server->origin[0];
1398 point[1] = ent->fields.server->origin[1];
1399 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1401 // DRESK - Support for Entity Contents Transition Event
1402 // NOTE: Some logic needed to be slightly re-ordered
1403 // to not affect performance and allow for the feature.
1405 // Acquire Super Contents Prior to Resets
1406 cont = SV_PointSuperContents(point);
1407 // Acquire Native Contents Here
1408 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1410 // DRESK - Support for Entity Contents Transition Event
1411 if(ent->fields.server->watertype)
1412 // Entity did NOT Spawn; Check
1413 SV_CheckContentsTransition(ent, nNativeContents);
1416 ent->fields.server->waterlevel = 0;
1417 ent->fields.server->watertype = CONTENTS_EMPTY;
1418 cont = SV_PointSuperContents(point);
1419 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1421 ent->fields.server->watertype = nNativeContents;
1422 ent->fields.server->waterlevel = 1;
1423 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1424 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1426 ent->fields.server->waterlevel = 2;
1427 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1428 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1429 ent->fields.server->waterlevel = 3;
1433 return ent->fields.server->waterlevel > 1;
1442 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1445 vec3_t forward, into, side;
1447 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1448 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1450 // cut the tangential velocity
1451 i = DotProduct (stepnormal, ent->fields.server->velocity);
1452 VectorScale (stepnormal, i, into);
1453 VectorSubtract (ent->fields.server->velocity, into, side);
1454 ent->fields.server->velocity[0] = side[0] * (1 + d);
1455 ent->fields.server->velocity[1] = side[1] * (1 + d);
1461 =====================
1464 Player has come to a dead stop, possibly due to the problem with limited
1465 float precision at some angle joins in the BSP hull.
1467 Try fixing by pushing one pixel in each direction.
1469 This is a hack, but in the interest of good gameplay...
1470 ======================
1472 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1477 VectorCopy (ent->fields.server->origin, oldorg);
1480 for (i=0 ; i<8 ; i++)
1482 // try pushing a little in an axial direction
1485 case 0: dir[0] = 2; dir[1] = 0; break;
1486 case 1: dir[0] = 0; dir[1] = 2; break;
1487 case 2: dir[0] = -2; dir[1] = 0; break;
1488 case 3: dir[0] = 0; dir[1] = -2; break;
1489 case 4: dir[0] = 2; dir[1] = 2; break;
1490 case 5: dir[0] = -2; dir[1] = 2; break;
1491 case 6: dir[0] = 2; dir[1] = -2; break;
1492 case 7: dir[0] = -2; dir[1] = -2; break;
1495 SV_PushEntity (ent, dir, false);
1497 // retry the original move
1498 ent->fields.server->velocity[0] = oldvel[0];
1499 ent->fields.server->velocity[1] = oldvel[1];
1500 ent->fields.server->velocity[2] = 0;
1501 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1503 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1504 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1506 Con_DPrint("TryUnstick - success.\n");
1510 // go back to the original pos and try again
1511 VectorCopy (oldorg, ent->fields.server->origin);
1515 VectorClear (ent->fields.server->velocity);
1516 Con_DPrint("TryUnstick - failure.\n");
1522 =====================
1525 Only used by players
1526 ======================
1528 void SV_WalkMove (prvm_edict_t *ent)
1530 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1531 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1534 // if frametime is 0 (due to client sending the same timestamp twice),
1536 if (sv.frametime <= 0)
1539 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1541 SV_CheckVelocity(ent);
1543 // do a regular slide move unless it looks like you ran into a step
1544 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1546 VectorCopy (ent->fields.server->origin, start_origin);
1547 VectorCopy (ent->fields.server->velocity, start_velocity);
1549 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1551 // if the move did not hit the ground at any point, we're not on ground
1553 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1555 SV_CheckVelocity(ent);
1557 VectorCopy(ent->fields.server->origin, originalmove_origin);
1558 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1559 originalmove_clip = clip;
1560 originalmove_flags = (int)ent->fields.server->flags;
1561 originalmove_groundentity = ent->fields.server->groundentity;
1563 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1566 if (sv_nostep.integer)
1569 // if move didn't block on a step, return
1572 // if move was not trying to move into the step, return
1573 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1576 if (ent->fields.server->movetype != MOVETYPE_FLY)
1578 // return if gibbed by a trigger
1579 if (ent->fields.server->movetype != MOVETYPE_WALK)
1582 // only step up while jumping if that is enabled
1583 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1584 if (!oldonground && ent->fields.server->waterlevel == 0)
1588 // try moving up and forward to go up a step
1589 // back to start pos
1590 VectorCopy (start_origin, ent->fields.server->origin);
1591 VectorCopy (start_velocity, ent->fields.server->velocity);
1594 VectorClear (upmove);
1595 upmove[2] = sv_stepheight.value;
1596 // FIXME: don't link?
1597 SV_PushEntity(ent, upmove, false);
1600 ent->fields.server->velocity[2] = 0;
1601 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1602 ent->fields.server->velocity[2] += start_velocity[2];
1604 SV_CheckVelocity(ent);
1606 // check for stuckness, possibly due to the limited precision of floats
1607 // in the clipping hulls
1609 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1610 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1612 //Con_Printf("wall\n");
1613 // stepping up didn't make any progress, revert to original move
1614 VectorCopy(originalmove_origin, ent->fields.server->origin);
1615 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1616 //clip = originalmove_clip;
1617 ent->fields.server->flags = originalmove_flags;
1618 ent->fields.server->groundentity = originalmove_groundentity;
1619 // now try to unstick if needed
1620 //clip = SV_TryUnstick (ent, oldvel);
1624 //Con_Printf("step - ");
1626 // extra friction based on view angle
1627 if (clip & 2 && sv_wallfriction.integer)
1628 SV_WallFriction (ent, stepnormal);
1630 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1631 else if (!sv_gameplayfix_stepdown.integer || ent->fields.server->waterlevel >= 3 || start_velocity[2] >= (1.0 / 32.0) || !oldonground || ((int)ent->fields.server->flags & FL_ONGROUND))
1635 VectorClear (downmove);
1636 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1637 // FIXME: don't link?
1638 downtrace = SV_PushEntity (ent, downmove, false);
1640 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1642 // this has been disabled so that you can't jump when you are stepping
1643 // up while already jumping (also known as the Quake2 double jump bug)
1645 // LordHavoc: disabled this check so you can walk on monsters/players
1646 //if (ent->fields.server->solid == SOLID_BSP)
1648 //Con_Printf("onground\n");
1649 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1650 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1656 //Con_Printf("slope\n");
1657 // if the push down didn't end up on good ground, use the move without
1658 // the step up. This happens near wall / slope combinations, and can
1659 // cause the player to hop up higher on a slope too steep to climb
1660 VectorCopy(originalmove_origin, ent->fields.server->origin);
1661 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1662 //clip = originalmove_clip;
1663 ent->fields.server->flags = originalmove_flags;
1664 ent->fields.server->groundentity = originalmove_groundentity;
1667 SV_CheckVelocity(ent);
1670 //============================================================================
1676 Entities that are "stuck" to another entity
1679 void SV_Physics_Follow (prvm_edict_t *ent)
1681 vec3_t vf, vr, vu, angles, v;
1685 if (!SV_RunThink (ent))
1688 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1689 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1690 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])
1692 // quick case for no rotation
1693 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1697 angles[0] = -ent->fields.server->punchangle[0];
1698 angles[1] = ent->fields.server->punchangle[1];
1699 angles[2] = ent->fields.server->punchangle[2];
1700 AngleVectors (angles, vf, vr, vu);
1701 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];
1702 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];
1703 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];
1704 angles[0] = -e->fields.server->angles[0];
1705 angles[1] = e->fields.server->angles[1];
1706 angles[2] = e->fields.server->angles[2];
1707 AngleVectors (angles, vf, vr, vu);
1708 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1709 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1710 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1712 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1713 SV_LinkEdict (ent, true);
1717 ==============================================================================
1721 ==============================================================================
1726 SV_CheckWaterTransition
1730 void SV_CheckWaterTransition (prvm_edict_t *ent)
1733 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1734 if (!ent->fields.server->watertype)
1736 // just spawned here
1737 ent->fields.server->watertype = cont;
1738 ent->fields.server->waterlevel = 1;
1742 // DRESK - Support for Entity Contents Transition Event
1743 // NOTE: Call here BEFORE updating the watertype below,
1744 // and suppress watersplash sound if a valid function
1745 // call was made to allow for custom "splash" sounds.
1746 if( !SV_CheckContentsTransition(ent, cont) )
1747 { // Contents Transition Function Invalid; Potentially Play Water Sound
1748 // check if the entity crossed into or out of water
1749 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1750 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1753 if (cont <= CONTENTS_WATER)
1755 ent->fields.server->watertype = cont;
1756 ent->fields.server->waterlevel = 1;
1760 ent->fields.server->watertype = CONTENTS_EMPTY;
1761 ent->fields.server->waterlevel = 0;
1769 Toss, bounce, and fly movement. When onground, do nothing.
1772 void SV_Physics_Toss (prvm_edict_t *ent)
1777 // if onground, return without moving
1778 if ((int)ent->fields.server->flags & FL_ONGROUND)
1780 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1782 // don't stick to ground if onground and moving upward
1783 ent->fields.server->flags -= FL_ONGROUND;
1785 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1787 // we can trust FL_ONGROUND if groundentity is world because it never moves
1790 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1792 // if ent was supported by a brush model on previous frame,
1793 // and groundentity is now freed, set groundentity to 0 (world)
1794 // which leaves it suspended in the air
1795 ent->fields.server->groundentity = 0;
1799 ent->priv.server->suspendedinairflag = false;
1801 SV_CheckVelocity (ent);
1804 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1805 SV_AddGravity (ent);
1808 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1811 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1812 trace = SV_PushEntity (ent, move, true);
1813 if (ent->priv.server->free)
1815 if (trace.bmodelstartsolid)
1817 // try to unstick the entity
1818 SV_UnstickEntity(ent);
1819 trace = SV_PushEntity (ent, move, false);
1820 if (ent->priv.server->free)
1824 if (trace.fraction < 1)
1826 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1828 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1829 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1831 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1834 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1835 // LordHavoc: fixed grenades not bouncing when fired down a slope
1836 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1838 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1839 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1841 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1842 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1843 VectorClear (ent->fields.server->velocity);
1844 VectorClear (ent->fields.server->avelocity);
1847 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1851 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1853 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1854 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1855 VectorClear (ent->fields.server->velocity);
1856 VectorClear (ent->fields.server->avelocity);
1859 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1864 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1865 if (trace.plane.normal[2] > 0.7)
1867 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1868 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1869 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1870 ent->priv.server->suspendedinairflag = true;
1871 VectorClear (ent->fields.server->velocity);
1872 VectorClear (ent->fields.server->avelocity);
1875 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1879 // check for in water
1880 SV_CheckWaterTransition (ent);
1884 ===============================================================================
1888 ===============================================================================
1895 Monsters freefall when they don't have a ground entity, otherwise
1896 all movement is done with discrete steps.
1898 This is also used for objects that have become still on the ground, but
1899 will fall if the floor is pulled out from under them.
1902 void SV_Physics_Step (prvm_edict_t *ent)
1904 int flags = (int)ent->fields.server->flags;
1905 // don't fall at all if fly/swim
1906 if (!(flags & (FL_FLY | FL_SWIM)))
1908 if (flags & FL_ONGROUND)
1910 // freefall if onground and moving upward
1911 // freefall if not standing on a world surface (it may be a lift or trap door)
1912 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1914 ent->fields.server->flags -= FL_ONGROUND;
1916 SV_CheckVelocity(ent);
1917 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1918 SV_LinkEdict(ent, true);
1923 // freefall if not onground
1924 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1927 SV_CheckVelocity(ent);
1928 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1929 SV_LinkEdict(ent, true);
1932 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1933 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1940 SV_CheckWaterTransition(ent);
1943 //============================================================================
1945 static void SV_Physics_Entity (prvm_edict_t *ent)
1947 // don't run a move on newly spawned projectiles as it messes up movement
1948 // interpolation and rocket trails
1949 qboolean runmove = ent->priv.server->move;
1950 ent->priv.server->move = true;
1951 switch ((int) ent->fields.server->movetype)
1954 case MOVETYPE_FAKEPUSH:
1955 SV_Physics_Pusher (ent);
1958 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1959 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1962 case MOVETYPE_FOLLOW:
1963 SV_Physics_Follow (ent);
1965 case MOVETYPE_NOCLIP:
1966 if (SV_RunThink(ent))
1969 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1970 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1972 SV_LinkEdict(ent, false);
1975 SV_Physics_Step (ent);
1978 if (SV_RunThink (ent))
1980 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1981 SV_AddGravity (ent);
1982 SV_CheckStuck (ent);
1984 SV_LinkEdict (ent, true);
1988 case MOVETYPE_BOUNCE:
1989 case MOVETYPE_BOUNCEMISSILE:
1990 case MOVETYPE_FLYMISSILE:
1993 if (SV_RunThink (ent) && runmove)
1994 SV_Physics_Toss (ent);
1997 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2002 void SV_Physics_ClientMove(void)
2005 ent = host_client->edict;
2007 // call player physics
2010 // call standard client pre-think
2011 prog->globals.server->time = sv.time;
2012 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2013 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2015 // make sure the velocity is sane (not a NaN)
2016 SV_CheckVelocity(ent);
2017 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2018 // player_run/player_stand1 does not horribly malfunction if the
2019 // velocity becomes a number that is both == 0 and != 0
2020 // (sounds to me like NaN but to be absolutely safe...)
2021 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2022 VectorClear(ent->fields.server->velocity);
2024 // perform MOVETYPE_WALK behavior
2025 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2026 SV_AddGravity (ent);
2027 SV_CheckStuck (ent);
2030 SV_CheckVelocity (ent);
2032 SV_LinkEdict (ent, true);
2034 SV_CheckVelocity (ent);
2036 // call standard player post-think
2037 prog->globals.server->time = sv.time;
2038 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2039 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2041 if(ent->fields.server->fixangle)
2043 // angle fixing was requested by physics code...
2044 // so store the current angles for later use
2045 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2046 host_client->fixangle_angles_set = TRUE;
2048 // and clear fixangle for the next frame
2049 ent->fields.server->fixangle = 0;
2053 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2055 // don't do physics on disconnected clients, FrikBot relies on this
2056 if (!host_client->spawned)
2058 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2062 // don't run physics here if running asynchronously
2063 if (host_client->clmovement_skipphysicsframes <= 0)
2066 // make sure the velocity is sane (not a NaN)
2067 SV_CheckVelocity(ent);
2068 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2069 // player_run/player_stand1 does not horribly malfunction if the
2070 // velocity becomes a number that is both == 0 and != 0
2071 // (sounds to me like NaN but to be absolutely safe...)
2072 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2073 VectorClear(ent->fields.server->velocity);
2075 // call standard client pre-think
2076 prog->globals.server->time = sv.time;
2077 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2078 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2079 SV_CheckVelocity (ent);
2081 switch ((int) ent->fields.server->movetype)
2084 case MOVETYPE_FAKEPUSH:
2085 SV_Physics_Pusher (ent);
2088 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2089 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2092 case MOVETYPE_FOLLOW:
2093 SV_Physics_Follow (ent);
2095 case MOVETYPE_NOCLIP:
2098 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2099 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2102 SV_Physics_Step (ent);
2106 // don't run physics here if running asynchronously
2107 if (host_client->clmovement_skipphysicsframes <= 0)
2109 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2110 SV_AddGravity (ent);
2111 SV_CheckStuck (ent);
2116 case MOVETYPE_BOUNCE:
2117 case MOVETYPE_BOUNCEMISSILE:
2118 case MOVETYPE_FLYMISSILE:
2121 SV_Physics_Toss (ent);
2125 SV_CheckWater (ent);
2129 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2133 // decrement the countdown variable used to decide when to go back to
2134 // synchronous physics
2135 if (host_client->clmovement_skipphysicsframes > 0)
2136 host_client->clmovement_skipphysicsframes--;
2138 SV_CheckVelocity (ent);
2140 SV_LinkEdict (ent, true);
2142 SV_CheckVelocity (ent);
2144 // call standard player post-think
2145 prog->globals.server->time = sv.time;
2146 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2147 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2149 if(ent->fields.server->fixangle)
2151 // angle fixing was requested by physics code...
2152 // so store the current angles for later use
2153 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2154 host_client->fixangle_angles_set = TRUE;
2156 // and clear fixangle for the next frame
2157 ent->fields.server->fixangle = 0;
2167 void SV_Physics (void)
2172 // let the progs know that a new frame has started
2173 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2174 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2175 prog->globals.server->time = sv.time;
2176 prog->globals.server->frametime = sv.frametime;
2177 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2180 // treat each object in turn
2183 // if force_retouch, relink all the entities
2184 if (prog->globals.server->force_retouch > 0)
2185 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2186 if (!ent->priv.server->free)
2187 SV_LinkEdict (ent, true); // force retouch even for stationary
2189 // run physics on the client entities
2190 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2191 if (!ent->priv.server->free)
2192 SV_Physics_ClientEntity(ent);
2194 // run physics on all the non-client entities
2195 if (!sv_freezenonclients.integer)
2196 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2197 if (!ent->priv.server->free)
2198 SV_Physics_Entity(ent);
2200 if (prog->globals.server->force_retouch > 0)
2201 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2203 // LordHavoc: endframe support
2204 if (prog->funcoffsets.EndFrame)
2206 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2207 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2208 prog->globals.server->time = sv.time;
2209 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2212 // decrement prog->num_edicts if the highest number entities died
2213 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2215 if (!sv_freezenonclients.integer)
2216 sv.time += sv.frametime;