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, vec3_t offset)
466 VectorAdd(ent->fields.server->origin, offset, org);
467 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
468 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
472 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
474 // q1bsp/hlbsp use hulls and if the entity does not exactly match
475 // a hull size it is incorrectly tested, so this code tries to
476 // 'fix' it slightly...
477 // FIXME: this breaks entities larger than the hull size
480 VectorAdd(org, ent->fields.server->mins, m1);
481 VectorAdd(org, ent->fields.server->maxs, m2);
482 VectorSubtract(m2, m1, s);
483 #define EPSILON (1.0f / 32.0f)
484 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
485 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
486 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
487 for (i = 0;i < 8;i++)
489 v[0] = (i & 1) ? m2[0] : m1[0];
490 v[1] = (i & 2) ? m2[1] : m1[1];
491 v[2] = (i & 4) ? m2[2] : m1[2];
492 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
497 // if the trace found a better position for the entity, move it there
498 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
499 VectorCopy(trace.endpos, ent->fields.server->origin);
508 void SV_CheckAllEnts (void)
513 // see if any solid entities are inside the final position
514 check = PRVM_NEXT_EDICT(prog->edicts);
515 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
517 if (check->priv.server->free)
519 if (check->fields.server->movetype == MOVETYPE_PUSH
520 || check->fields.server->movetype == MOVETYPE_NONE
521 || check->fields.server->movetype == MOVETYPE_FOLLOW
522 || check->fields.server->movetype == MOVETYPE_NOCLIP)
525 if (SV_TestEntityPosition (check, vec3_origin))
526 Con_Print("entity in invalid position\n");
530 // DRESK - Support for Entity Contents Transition Event
533 SV_CheckContentsTransition
535 returns true if entity had a valid contentstransition function call
538 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
540 int bValidFunctionCall;
541 prvm_eval_t *contentstransition;
543 // Default Valid Function Call to False
544 bValidFunctionCall = false;
546 if(ent->fields.server->watertype != nContents)
547 { // Changed Contents
548 // Acquire Contents Transition Function from QC
549 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
551 if(contentstransition->function)
552 { // Valid Function; Execute
553 // Assign Valid Function
554 bValidFunctionCall = true;
555 // Prepare Parameters (Original Contents, New Contents)
557 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
559 PRVM_G_FLOAT(OFS_PARM1) = nContents;
560 // Execute VM Function
561 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
565 // Return if Function Call was Valid
566 return bValidFunctionCall;
575 void SV_CheckVelocity (prvm_edict_t *ent)
583 for (i=0 ; i<3 ; i++)
585 if (IS_NAN(ent->fields.server->velocity[i]))
587 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
588 ent->fields.server->velocity[i] = 0;
590 if (IS_NAN(ent->fields.server->origin[i]))
592 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
593 ent->fields.server->origin[i] = 0;
597 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
598 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
599 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
601 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
602 ent->fields.server->velocity[0] *= wishspeed;
603 ent->fields.server->velocity[1] *= wishspeed;
604 ent->fields.server->velocity[2] *= wishspeed;
612 Runs thinking code if time. There is some play in the exact time the think
613 function will be called, because it is called before any movement is done
614 in a frame. Not used for pushmove objects, because they must be exact.
615 Returns false if the entity removed itself.
618 qboolean SV_RunThink (prvm_edict_t *ent)
622 thinktime = ent->fields.server->nextthink;
623 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
626 // don't let things stay in the past.
627 // it is possible to start that way by a trigger with a local time.
628 if (thinktime < sv.time)
631 ent->fields.server->nextthink = 0;
632 prog->globals.server->time = thinktime;
633 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
634 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
635 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
636 return !ent->priv.server->free;
643 Two entities have touched, so run their touch functions
646 extern void VM_SetTraceGlobals(const trace_t *trace);
647 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
649 int old_self, old_other;
650 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
653 old_self = prog->globals.server->self;
654 old_other = prog->globals.server->other;
656 VM_SetTraceGlobals(trace);
658 prog->globals.server->time = sv.time;
659 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
661 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
662 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
663 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
666 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
668 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
669 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
670 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
671 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
672 prog->globals.server->trace_plane_dist = -trace->plane.dist;
673 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
674 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
676 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
678 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
680 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
682 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
685 prog->globals.server->self = old_self;
686 prog->globals.server->other = old_other;
694 Slide off of the impacting object
695 returns the blocked flags (1 = floor, 2 = step / wall)
698 #define STOP_EPSILON 0.1
699 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
704 backoff = -DotProduct (in, normal) * overbounce;
705 VectorMA(in, backoff, normal, out);
707 for (i = 0;i < 3;i++)
708 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
717 The basic solid body movement clip that slides along multiple planes
718 Returns the clipflags if the velocity was modified (hit something solid)
722 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
725 // LordHavoc: increased from 5 to 32
726 #define MAX_CLIP_PLANES 32
727 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
729 int blocked, bumpcount;
730 int i, j, impact, numplanes;
732 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
737 VectorCopy(ent->fields.server->velocity, original_velocity);
738 VectorCopy(ent->fields.server->velocity, primal_velocity);
741 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
743 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
746 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
747 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
749 //if (trace.fraction < 0.002)
754 VectorCopy(ent->fields.server->origin, start);
755 start[2] += 3;//0.03125;
756 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
757 end[2] += 3;//0.03125;
758 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
759 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)))
761 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
767 for (i = 0;i < numplanes;i++)
769 VectorCopy(ent->fields.server->origin, start);
770 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
771 VectorMA(start, 3, planes[i], start);
772 VectorMA(end, 3, planes[i], end);
773 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
774 if (trace.fraction < testtrace.fraction)
777 VectorCopy(start, ent->fields.server->origin);
782 // VectorAdd(ent->fields.server->origin, planes[j], start);
788 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);
789 if (trace.fraction < 1)
790 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
795 if (trace.bmodelstartsolid)
797 // LordHavoc: note: this code is what makes entities stick in place
798 // if embedded in world only (you can walk through other objects if
800 // entity is trapped in another solid
801 VectorClear(ent->fields.server->velocity);
806 // break if it moved the entire distance
807 if (trace.fraction == 1)
809 VectorCopy(trace.endpos, ent->fields.server->origin);
815 Con_Printf ("SV_FlyMove: !trace.ent");
816 trace.ent = prog->edicts;
819 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
821 if (trace.plane.normal[2])
823 if (trace.plane.normal[2] > 0.7)
827 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
828 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
835 // save the trace for player extrafriction
837 VectorCopy(trace.plane.normal, stepnormal);
840 if (trace.fraction >= 0.001)
842 // actually covered some distance
843 VectorCopy(trace.endpos, ent->fields.server->origin);
844 VectorCopy(ent->fields.server->velocity, original_velocity);
848 // run the impact function
851 SV_Impact(ent, &trace);
853 // break if removed by the impact function
854 if (ent->priv.server->free)
858 time_left *= 1 - trace.fraction;
860 // clipped to another plane
861 if (numplanes >= MAX_CLIP_PLANES)
863 // this shouldn't really happen
864 VectorClear(ent->fields.server->velocity);
870 for (i = 0;i < numplanes;i++)
871 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
875 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
880 VectorCopy(trace.plane.normal, planes[numplanes]);
883 if (sv_newflymove.integer)
884 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
887 // modify original_velocity so it parallels all of the clip planes
888 for (i = 0;i < numplanes;i++)
890 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
891 for (j = 0;j < numplanes;j++)
896 if (DotProduct(new_velocity, planes[j]) < 0)
906 // go along this plane
907 VectorCopy(new_velocity, ent->fields.server->velocity);
911 // go along the crease
914 VectorClear(ent->fields.server->velocity);
918 CrossProduct(planes[0], planes[1], dir);
919 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
920 VectorNormalize(dir);
921 d = DotProduct(dir, ent->fields.server->velocity);
922 VectorScale(dir, d, ent->fields.server->velocity);
926 // if current velocity is against the original velocity,
927 // stop dead to avoid tiny occilations in sloping corners
928 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
930 VectorClear(ent->fields.server->velocity);
935 //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]);
938 if ((blocked & 1) == 0 && bumpcount > 1)
940 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
941 // flag ONGROUND if there's ground under it
942 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
946 // LordHavoc: this came from QW and allows you to get out of water more easily
947 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
948 VectorCopy(primal_velocity, ent->fields.server->velocity);
958 void SV_AddGravity (prvm_edict_t *ent)
963 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
964 if (val!=0 && val->_float)
965 ent_gravity = val->_float;
968 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
973 ===============================================================================
977 ===============================================================================
984 Does not change the entities velocity at all
987 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
993 VectorAdd (ent->fields.server->origin, push, end);
995 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
997 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
998 type = MOVE_NOMONSTERS; // only clip against bmodels
1002 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1003 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1006 VectorCopy (trace.endpos, ent->fields.server->origin);
1007 SV_LinkEdict (ent, true);
1009 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)))
1010 SV_Impact (ent, &trace);
1021 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1024 float savesolid, movetime2, pushltime;
1025 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1027 int numcheckentities;
1028 static prvm_edict_t *checkentities[MAX_EDICTS];
1029 model_t *pushermodel;
1031 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1033 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])
1035 pusher->fields.server->ltime += movetime;
1039 switch ((int) pusher->fields.server->solid)
1041 // LordHavoc: valid pusher types
1044 case SOLID_SLIDEBOX:
1045 case SOLID_CORPSE: // LordHavoc: this would be weird...
1047 // LordHavoc: no collisions
1050 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1051 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1052 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1053 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1054 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1055 pusher->fields.server->ltime += movetime;
1056 SV_LinkEdict (pusher, false);
1059 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1062 index = (int) pusher->fields.server->modelindex;
1063 if (index < 1 || index >= MAX_MODELS)
1065 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1068 pushermodel = sv.models[index];
1070 movetime2 = movetime;
1071 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1072 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1073 if (moveangle[0] || moveangle[2])
1075 for (i = 0;i < 3;i++)
1079 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1080 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1084 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1085 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1089 else if (moveangle[1])
1091 for (i = 0;i < 3;i++)
1095 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1096 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1100 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1101 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1107 for (i = 0;i < 3;i++)
1111 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1112 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1116 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1117 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1122 VectorNegate (moveangle, a);
1123 AngleVectorsFLU (a, forward, left, up);
1125 VectorCopy (pusher->fields.server->origin, pushorig);
1126 VectorCopy (pusher->fields.server->angles, pushang);
1127 pushltime = pusher->fields.server->ltime;
1129 // move the pusher to its final position
1131 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1132 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1133 pusher->fields.server->ltime += movetime;
1134 SV_LinkEdict (pusher, false);
1137 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1138 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1139 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);
1140 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1142 savesolid = pusher->fields.server->solid;
1144 // see if any solid entities are inside the final position
1147 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1148 for (e = 0;e < numcheckentities;e++)
1150 prvm_edict_t *check = checkentities[e];
1151 int checkcontents = SV_GenericHitSuperContentsMask(check);
1152 if (check->fields.server->movetype == MOVETYPE_NONE
1153 || check->fields.server->movetype == MOVETYPE_PUSH
1154 || check->fields.server->movetype == MOVETYPE_FOLLOW
1155 || check->fields.server->movetype == MOVETYPE_NOCLIP
1156 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1159 // if the entity is standing on the pusher, it will definitely be moved
1160 // if the entity is not standing on the pusher, but is in the pusher's
1161 // final position, move it
1162 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1164 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, checkcontents);
1165 if (!trace.startsolid)
1170 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1173 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1174 org2[0] = DotProduct (org, forward);
1175 org2[1] = DotProduct (org, left);
1176 org2[2] = DotProduct (org, up);
1177 VectorSubtract (org2, org, move);
1178 VectorAdd (move, move1, move);
1181 VectorCopy (move1, move);
1183 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1184 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1185 sv.moved_edicts[num_moved++] = check;
1187 // try moving the contacted entity
1188 pusher->fields.server->solid = SOLID_NOT;
1189 trace = SV_PushEntity (check, move, true);
1190 // FIXME: turn players specially
1191 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1192 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1193 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1195 // this trace.fraction < 1 check causes items to fall off of pushers
1196 // if they pass under or through a wall
1197 // the groundentity check causes items to fall off of ledges
1198 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1199 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1201 // if it is still inside the pusher, block
1202 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, checkcontents);
1203 if (trace.startsolid)
1205 // try moving the contacted entity a tiny bit further to account for precision errors
1207 pusher->fields.server->solid = SOLID_NOT;
1208 VectorScale(move, 1.1, move2);
1209 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1210 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1211 SV_PushEntity (check, move2, true);
1212 pusher->fields.server->solid = savesolid;
1213 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, checkcontents);
1214 if (trace.startsolid)
1216 // try moving the contacted entity a tiny bit less to account for precision errors
1217 pusher->fields.server->solid = SOLID_NOT;
1218 VectorScale(move, 0.9, move2);
1219 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1220 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1221 SV_PushEntity (check, move2, true);
1222 pusher->fields.server->solid = savesolid;
1223 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, checkcontents);
1224 if (trace.startsolid)
1226 // still inside pusher, so it's really blocked
1229 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1231 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1234 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1235 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1239 VectorCopy (pushorig, pusher->fields.server->origin);
1240 VectorCopy (pushang, pusher->fields.server->angles);
1241 pusher->fields.server->ltime = pushltime;
1242 SV_LinkEdict (pusher, false);
1244 // move back any entities we already moved
1245 for (i = 0;i < num_moved;i++)
1247 prvm_edict_t *ed = sv.moved_edicts[i];
1248 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1249 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1250 SV_LinkEdict (ed, false);
1253 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1254 if (pusher->fields.server->blocked)
1256 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1257 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1258 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1265 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1266 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1267 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1276 void SV_Physics_Pusher (prvm_edict_t *ent)
1278 float thinktime, oldltime, movetime;
1280 oldltime = ent->fields.server->ltime;
1282 thinktime = ent->fields.server->nextthink;
1283 if (thinktime < ent->fields.server->ltime + sv.frametime)
1285 movetime = thinktime - ent->fields.server->ltime;
1290 movetime = sv.frametime;
1293 // advances ent->fields.server->ltime if not blocked
1294 SV_PushMove (ent, movetime);
1296 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1298 ent->fields.server->nextthink = 0;
1299 prog->globals.server->time = sv.time;
1300 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1301 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1302 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1308 ===============================================================================
1312 ===============================================================================
1315 static float unstickoffsets[] =
1349 This is a big hack to try and fix the rare case of getting stuck in the world
1353 void SV_CheckStuck (prvm_edict_t *ent)
1358 if (!SV_TestEntityPosition(ent, vec3_origin))
1360 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1364 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1366 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1368 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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1369 SV_LinkEdict (ent, true);
1374 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1375 if (!SV_TestEntityPosition(ent, offset))
1377 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1378 SV_LinkEdict (ent, true);
1382 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1385 static void SV_UnstickEntity (prvm_edict_t *ent)
1389 // if not stuck in a bmodel, just return
1390 if (!SV_TestEntityPosition(ent, vec3_origin))
1393 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1395 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1397 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), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1398 SV_LinkEdict (ent, true);
1403 if (developer.integer >= 100)
1404 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1413 qboolean SV_CheckWater (prvm_edict_t *ent)
1416 int nNativeContents;
1419 point[0] = ent->fields.server->origin[0];
1420 point[1] = ent->fields.server->origin[1];
1421 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1423 // DRESK - Support for Entity Contents Transition Event
1424 // NOTE: Some logic needed to be slightly re-ordered
1425 // to not affect performance and allow for the feature.
1427 // Acquire Super Contents Prior to Resets
1428 cont = SV_PointSuperContents(point);
1429 // Acquire Native Contents Here
1430 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1432 // DRESK - Support for Entity Contents Transition Event
1433 if(ent->fields.server->watertype)
1434 // Entity did NOT Spawn; Check
1435 SV_CheckContentsTransition(ent, nNativeContents);
1438 ent->fields.server->waterlevel = 0;
1439 ent->fields.server->watertype = CONTENTS_EMPTY;
1440 cont = SV_PointSuperContents(point);
1441 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1443 ent->fields.server->watertype = nNativeContents;
1444 ent->fields.server->waterlevel = 1;
1445 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1446 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1448 ent->fields.server->waterlevel = 2;
1449 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1450 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1451 ent->fields.server->waterlevel = 3;
1455 return ent->fields.server->waterlevel > 1;
1464 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1467 vec3_t forward, into, side;
1469 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1470 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1472 // cut the tangential velocity
1473 i = DotProduct (stepnormal, ent->fields.server->velocity);
1474 VectorScale (stepnormal, i, into);
1475 VectorSubtract (ent->fields.server->velocity, into, side);
1476 ent->fields.server->velocity[0] = side[0] * (1 + d);
1477 ent->fields.server->velocity[1] = side[1] * (1 + d);
1483 =====================
1486 Player has come to a dead stop, possibly due to the problem with limited
1487 float precision at some angle joins in the BSP hull.
1489 Try fixing by pushing one pixel in each direction.
1491 This is a hack, but in the interest of good gameplay...
1492 ======================
1494 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1499 VectorCopy (ent->fields.server->origin, oldorg);
1502 for (i=0 ; i<8 ; i++)
1504 // try pushing a little in an axial direction
1507 case 0: dir[0] = 2; dir[1] = 0; break;
1508 case 1: dir[0] = 0; dir[1] = 2; break;
1509 case 2: dir[0] = -2; dir[1] = 0; break;
1510 case 3: dir[0] = 0; dir[1] = -2; break;
1511 case 4: dir[0] = 2; dir[1] = 2; break;
1512 case 5: dir[0] = -2; dir[1] = 2; break;
1513 case 6: dir[0] = 2; dir[1] = -2; break;
1514 case 7: dir[0] = -2; dir[1] = -2; break;
1517 SV_PushEntity (ent, dir, false);
1519 // retry the original move
1520 ent->fields.server->velocity[0] = oldvel[0];
1521 ent->fields.server->velocity[1] = oldvel[1];
1522 ent->fields.server->velocity[2] = 0;
1523 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1525 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1526 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1528 Con_DPrint("TryUnstick - success.\n");
1532 // go back to the original pos and try again
1533 VectorCopy (oldorg, ent->fields.server->origin);
1537 VectorClear (ent->fields.server->velocity);
1538 Con_DPrint("TryUnstick - failure.\n");
1544 =====================
1547 Only used by players
1548 ======================
1550 void SV_WalkMove (prvm_edict_t *ent)
1552 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1553 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1556 // if frametime is 0 (due to client sending the same timestamp twice),
1558 if (sv.frametime <= 0)
1561 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1563 SV_CheckVelocity(ent);
1565 // do a regular slide move unless it looks like you ran into a step
1566 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1568 VectorCopy (ent->fields.server->origin, start_origin);
1569 VectorCopy (ent->fields.server->velocity, start_velocity);
1571 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1573 // if the move did not hit the ground at any point, we're not on ground
1575 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1577 SV_CheckVelocity(ent);
1579 VectorCopy(ent->fields.server->origin, originalmove_origin);
1580 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1581 originalmove_clip = clip;
1582 originalmove_flags = (int)ent->fields.server->flags;
1583 originalmove_groundentity = ent->fields.server->groundentity;
1585 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1588 if (sv_nostep.integer)
1591 // if move didn't block on a step, return
1594 // if move was not trying to move into the step, return
1595 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1598 if (ent->fields.server->movetype != MOVETYPE_FLY)
1600 // return if gibbed by a trigger
1601 if (ent->fields.server->movetype != MOVETYPE_WALK)
1604 // only step up while jumping if that is enabled
1605 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1606 if (!oldonground && ent->fields.server->waterlevel == 0)
1610 // try moving up and forward to go up a step
1611 // back to start pos
1612 VectorCopy (start_origin, ent->fields.server->origin);
1613 VectorCopy (start_velocity, ent->fields.server->velocity);
1616 VectorClear (upmove);
1617 upmove[2] = sv_stepheight.value;
1618 // FIXME: don't link?
1619 SV_PushEntity(ent, upmove, false);
1622 ent->fields.server->velocity[2] = 0;
1623 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1624 ent->fields.server->velocity[2] += start_velocity[2];
1626 SV_CheckVelocity(ent);
1628 // check for stuckness, possibly due to the limited precision of floats
1629 // in the clipping hulls
1631 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1632 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1634 //Con_Printf("wall\n");
1635 // stepping up didn't make any progress, revert to original move
1636 VectorCopy(originalmove_origin, ent->fields.server->origin);
1637 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1638 //clip = originalmove_clip;
1639 ent->fields.server->flags = originalmove_flags;
1640 ent->fields.server->groundentity = originalmove_groundentity;
1641 // now try to unstick if needed
1642 //clip = SV_TryUnstick (ent, oldvel);
1646 //Con_Printf("step - ");
1648 // extra friction based on view angle
1649 if (clip & 2 && sv_wallfriction.integer)
1650 SV_WallFriction (ent, stepnormal);
1652 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1653 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))
1657 VectorClear (downmove);
1658 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1659 // FIXME: don't link?
1660 downtrace = SV_PushEntity (ent, downmove, false);
1662 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1664 // this has been disabled so that you can't jump when you are stepping
1665 // up while already jumping (also known as the Quake2 double jump bug)
1667 // LordHavoc: disabled this check so you can walk on monsters/players
1668 //if (ent->fields.server->solid == SOLID_BSP)
1670 //Con_Printf("onground\n");
1671 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1672 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1678 //Con_Printf("slope\n");
1679 // if the push down didn't end up on good ground, use the move without
1680 // the step up. This happens near wall / slope combinations, and can
1681 // cause the player to hop up higher on a slope too steep to climb
1682 VectorCopy(originalmove_origin, ent->fields.server->origin);
1683 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1684 //clip = originalmove_clip;
1685 ent->fields.server->flags = originalmove_flags;
1686 ent->fields.server->groundentity = originalmove_groundentity;
1689 SV_CheckVelocity(ent);
1692 //============================================================================
1698 Entities that are "stuck" to another entity
1701 void SV_Physics_Follow (prvm_edict_t *ent)
1703 vec3_t vf, vr, vu, angles, v;
1707 if (!SV_RunThink (ent))
1710 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1711 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1712 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])
1714 // quick case for no rotation
1715 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1719 angles[0] = -ent->fields.server->punchangle[0];
1720 angles[1] = ent->fields.server->punchangle[1];
1721 angles[2] = ent->fields.server->punchangle[2];
1722 AngleVectors (angles, vf, vr, vu);
1723 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];
1724 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];
1725 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];
1726 angles[0] = -e->fields.server->angles[0];
1727 angles[1] = e->fields.server->angles[1];
1728 angles[2] = e->fields.server->angles[2];
1729 AngleVectors (angles, vf, vr, vu);
1730 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1731 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1732 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1734 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1735 SV_LinkEdict (ent, true);
1739 ==============================================================================
1743 ==============================================================================
1748 SV_CheckWaterTransition
1752 void SV_CheckWaterTransition (prvm_edict_t *ent)
1755 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1756 if (!ent->fields.server->watertype)
1758 // just spawned here
1759 ent->fields.server->watertype = cont;
1760 ent->fields.server->waterlevel = 1;
1764 // DRESK - Support for Entity Contents Transition Event
1765 // NOTE: Call here BEFORE updating the watertype below,
1766 // and suppress watersplash sound if a valid function
1767 // call was made to allow for custom "splash" sounds.
1768 if( !SV_CheckContentsTransition(ent, cont) )
1769 { // Contents Transition Function Invalid; Potentially Play Water Sound
1770 // check if the entity crossed into or out of water
1771 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1772 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1775 if (cont <= CONTENTS_WATER)
1777 ent->fields.server->watertype = cont;
1778 ent->fields.server->waterlevel = 1;
1782 ent->fields.server->watertype = CONTENTS_EMPTY;
1783 ent->fields.server->waterlevel = 0;
1791 Toss, bounce, and fly movement. When onground, do nothing.
1794 void SV_Physics_Toss (prvm_edict_t *ent)
1799 // if onground, return without moving
1800 if ((int)ent->fields.server->flags & FL_ONGROUND)
1802 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1804 // don't stick to ground if onground and moving upward
1805 ent->fields.server->flags -= FL_ONGROUND;
1807 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1809 // we can trust FL_ONGROUND if groundentity is world because it never moves
1812 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1814 // if ent was supported by a brush model on previous frame,
1815 // and groundentity is now freed, set groundentity to 0 (world)
1816 // which leaves it suspended in the air
1817 ent->fields.server->groundentity = 0;
1821 ent->priv.server->suspendedinairflag = false;
1823 SV_CheckVelocity (ent);
1826 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1827 SV_AddGravity (ent);
1830 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1833 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1834 trace = SV_PushEntity (ent, move, true);
1835 if (ent->priv.server->free)
1837 if (trace.bmodelstartsolid)
1839 // try to unstick the entity
1840 SV_UnstickEntity(ent);
1841 trace = SV_PushEntity (ent, move, false);
1842 if (ent->priv.server->free)
1846 if (trace.fraction < 1)
1848 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1850 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1851 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1853 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1856 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1857 // LordHavoc: fixed grenades not bouncing when fired down a slope
1858 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1860 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1861 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1863 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1864 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1865 VectorClear (ent->fields.server->velocity);
1866 VectorClear (ent->fields.server->avelocity);
1869 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1873 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1875 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1876 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1877 VectorClear (ent->fields.server->velocity);
1878 VectorClear (ent->fields.server->avelocity);
1881 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1886 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1887 if (trace.plane.normal[2] > 0.7)
1889 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1890 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1891 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1892 ent->priv.server->suspendedinairflag = true;
1893 VectorClear (ent->fields.server->velocity);
1894 VectorClear (ent->fields.server->avelocity);
1897 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1901 // check for in water
1902 SV_CheckWaterTransition (ent);
1906 ===============================================================================
1910 ===============================================================================
1917 Monsters freefall when they don't have a ground entity, otherwise
1918 all movement is done with discrete steps.
1920 This is also used for objects that have become still on the ground, but
1921 will fall if the floor is pulled out from under them.
1924 void SV_Physics_Step (prvm_edict_t *ent)
1926 int flags = (int)ent->fields.server->flags;
1927 // don't fall at all if fly/swim
1928 if (!(flags & (FL_FLY | FL_SWIM)))
1930 if (flags & FL_ONGROUND)
1932 // freefall if onground and moving upward
1933 // freefall if not standing on a world surface (it may be a lift or trap door)
1934 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1936 ent->fields.server->flags -= FL_ONGROUND;
1938 SV_CheckVelocity(ent);
1939 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1940 SV_LinkEdict(ent, true);
1945 // freefall if not onground
1946 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1949 SV_CheckVelocity(ent);
1950 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1951 SV_LinkEdict(ent, true);
1954 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1955 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1962 SV_CheckWaterTransition(ent);
1965 //============================================================================
1967 static void SV_Physics_Entity (prvm_edict_t *ent)
1969 // don't run a move on newly spawned projectiles as it messes up movement
1970 // interpolation and rocket trails
1971 qboolean runmove = ent->priv.server->move;
1972 ent->priv.server->move = true;
1973 switch ((int) ent->fields.server->movetype)
1976 case MOVETYPE_FAKEPUSH:
1977 SV_Physics_Pusher (ent);
1980 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1981 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1984 case MOVETYPE_FOLLOW:
1985 SV_Physics_Follow (ent);
1987 case MOVETYPE_NOCLIP:
1988 if (SV_RunThink(ent))
1991 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1992 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1994 SV_LinkEdict(ent, false);
1997 SV_Physics_Step (ent);
2000 if (SV_RunThink (ent))
2002 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2003 SV_AddGravity (ent);
2004 SV_CheckStuck (ent);
2006 SV_LinkEdict (ent, true);
2010 case MOVETYPE_BOUNCE:
2011 case MOVETYPE_BOUNCEMISSILE:
2012 case MOVETYPE_FLYMISSILE:
2015 if (SV_RunThink (ent) && runmove)
2016 SV_Physics_Toss (ent);
2019 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2024 void SV_Physics_ClientMove(void)
2027 ent = host_client->edict;
2029 // call player physics, this needs the proper frametime
2030 prog->globals.server->frametime = sv.frametime;
2033 // call standard client pre-think, with frametime = 0
2034 prog->globals.server->time = sv.time;
2035 prog->globals.server->frametime = 0;
2036 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2037 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2038 prog->globals.server->frametime = sv.frametime;
2040 // make sure the velocity is sane (not a NaN)
2041 SV_CheckVelocity(ent);
2042 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2043 // player_run/player_stand1 does not horribly malfunction if the
2044 // velocity becomes a number that is both == 0 and != 0
2045 // (sounds to me like NaN but to be absolutely safe...)
2046 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2047 VectorClear(ent->fields.server->velocity);
2049 // perform MOVETYPE_WALK behavior
2050 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2051 SV_AddGravity (ent);
2052 SV_CheckStuck (ent);
2055 SV_CheckVelocity (ent);
2057 SV_LinkEdict (ent, true);
2059 SV_CheckVelocity (ent);
2061 // call standard player post-think, with frametime = 0
2062 prog->globals.server->time = sv.time;
2063 prog->globals.server->frametime = 0;
2064 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2065 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2066 prog->globals.server->frametime = sv.frametime;
2068 if(ent->fields.server->fixangle)
2070 // angle fixing was requested by physics code...
2071 // so store the current angles for later use
2072 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2073 host_client->fixangle_angles_set = TRUE;
2075 // and clear fixangle for the next frame
2076 ent->fields.server->fixangle = 0;
2080 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2082 // don't do physics on disconnected clients, FrikBot relies on this
2083 if (!host_client->spawned)
2085 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2089 // don't run physics here if running asynchronously
2090 if (host_client->clmovement_skipphysicsframes <= 0)
2093 // make sure the velocity is sane (not a NaN)
2094 SV_CheckVelocity(ent);
2095 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2096 // player_run/player_stand1 does not horribly malfunction if the
2097 // velocity becomes a number that is both == 0 and != 0
2098 // (sounds to me like NaN but to be absolutely safe...)
2099 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2100 VectorClear(ent->fields.server->velocity);
2102 // call standard client pre-think
2103 prog->globals.server->time = sv.time;
2104 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2105 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2106 SV_CheckVelocity (ent);
2108 switch ((int) ent->fields.server->movetype)
2111 case MOVETYPE_FAKEPUSH:
2112 SV_Physics_Pusher (ent);
2115 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2116 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2119 case MOVETYPE_FOLLOW:
2120 SV_Physics_Follow (ent);
2122 case MOVETYPE_NOCLIP:
2125 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2126 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2129 SV_Physics_Step (ent);
2133 // don't run physics here if running asynchronously
2134 if (host_client->clmovement_skipphysicsframes <= 0)
2136 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2137 SV_AddGravity (ent);
2138 SV_CheckStuck (ent);
2143 case MOVETYPE_BOUNCE:
2144 case MOVETYPE_BOUNCEMISSILE:
2145 case MOVETYPE_FLYMISSILE:
2148 SV_Physics_Toss (ent);
2152 SV_CheckWater (ent);
2156 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2160 // decrement the countdown variable used to decide when to go back to
2161 // synchronous physics
2162 if (host_client->clmovement_skipphysicsframes > 0)
2163 host_client->clmovement_skipphysicsframes--;
2165 SV_CheckVelocity (ent);
2167 SV_LinkEdict (ent, true);
2169 SV_CheckVelocity (ent);
2171 // call standard player post-think
2172 prog->globals.server->time = sv.time;
2173 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2174 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2176 if(ent->fields.server->fixangle)
2178 // angle fixing was requested by physics code...
2179 // so store the current angles for later use
2180 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2181 host_client->fixangle_angles_set = TRUE;
2183 // and clear fixangle for the next frame
2184 ent->fields.server->fixangle = 0;
2194 void SV_Physics (void)
2199 // let the progs know that a new frame has started
2200 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2201 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2202 prog->globals.server->time = sv.time;
2203 prog->globals.server->frametime = sv.frametime;
2204 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2207 // treat each object in turn
2210 // if force_retouch, relink all the entities
2211 if (prog->globals.server->force_retouch > 0)
2212 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2213 if (!ent->priv.server->free)
2214 SV_LinkEdict (ent, true); // force retouch even for stationary
2216 // run physics on the client entities
2217 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2218 if (!ent->priv.server->free)
2219 SV_Physics_ClientEntity(ent);
2221 // run physics on all the non-client entities
2222 if (!sv_freezenonclients.integer)
2223 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2224 if (!ent->priv.server->free)
2225 SV_Physics_Entity(ent);
2227 if (prog->globals.server->force_retouch > 0)
2228 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2230 // LordHavoc: endframe support
2231 if (prog->funcoffsets.EndFrame)
2233 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2234 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2235 prog->globals.server->time = sv.time;
2236 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2239 // decrement prog->num_edicts if the highest number entities died
2240 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2242 if (!sv_freezenonclients.integer)
2243 sv.time += sv.frametime;