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 if (check->fields.server->movetype == MOVETYPE_NONE
1152 || check->fields.server->movetype == MOVETYPE_PUSH
1153 || check->fields.server->movetype == MOVETYPE_FOLLOW
1154 || check->fields.server->movetype == MOVETYPE_NOCLIP
1155 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1158 // if the entity is standing on the pusher, it will definitely be moved
1159 // if the entity is not standing on the pusher, but is in the pusher's
1160 // final position, move it
1161 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1163 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);
1164 if (!trace.startsolid)
1169 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1172 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1173 org2[0] = DotProduct (org, forward);
1174 org2[1] = DotProduct (org, left);
1175 org2[2] = DotProduct (org, up);
1176 VectorSubtract (org2, org, move);
1177 VectorAdd (move, move1, move);
1180 VectorCopy (move1, move);
1182 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1183 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1184 sv.moved_edicts[num_moved++] = check;
1186 // try moving the contacted entity
1187 pusher->fields.server->solid = SOLID_NOT;
1188 trace = SV_PushEntity (check, move, true);
1189 // FIXME: turn players specially
1190 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1191 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1192 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1194 // this check is for items riding platforms that are passing under (or
1195 // through) walls intended to knock the items off
1196 if (trace.fraction < 1 && check->fields.server->movetype != MOVETYPE_WALK)
1197 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1199 // if it is still inside the pusher, block
1200 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);
1201 if (trace.startsolid)
1203 // try moving the contacted entity a tiny bit further to account for precision errors
1205 pusher->fields.server->solid = SOLID_NOT;
1206 VectorScale(move, 1.1, move2);
1207 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1208 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1209 SV_PushEntity (check, move2, true);
1210 pusher->fields.server->solid = savesolid;
1211 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);
1212 if (trace.startsolid)
1214 // try moving the contacted entity a tiny bit less to account for precision errors
1215 pusher->fields.server->solid = SOLID_NOT;
1216 VectorScale(move, 0.9, move2);
1217 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1218 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1219 SV_PushEntity (check, move2, true);
1220 pusher->fields.server->solid = savesolid;
1221 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);
1222 if (trace.startsolid)
1224 // still inside pusher, so it's really blocked
1227 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1229 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1232 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1233 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1237 VectorCopy (pushorig, pusher->fields.server->origin);
1238 VectorCopy (pushang, pusher->fields.server->angles);
1239 pusher->fields.server->ltime = pushltime;
1240 SV_LinkEdict (pusher, false);
1242 // move back any entities we already moved
1243 for (i = 0;i < num_moved;i++)
1245 prvm_edict_t *ed = sv.moved_edicts[i];
1246 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1247 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1248 SV_LinkEdict (ed, false);
1251 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1252 if (pusher->fields.server->blocked)
1254 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1255 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1256 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1263 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1264 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1265 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1274 void SV_Physics_Pusher (prvm_edict_t *ent)
1276 float thinktime, oldltime, movetime;
1278 oldltime = ent->fields.server->ltime;
1280 thinktime = ent->fields.server->nextthink;
1281 if (thinktime < ent->fields.server->ltime + sv.frametime)
1283 movetime = thinktime - ent->fields.server->ltime;
1288 movetime = sv.frametime;
1291 // advances ent->fields.server->ltime if not blocked
1292 SV_PushMove (ent, movetime);
1294 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1296 ent->fields.server->nextthink = 0;
1297 prog->globals.server->time = sv.time;
1298 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1299 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1300 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1306 ===============================================================================
1310 ===============================================================================
1313 static float unstickoffsets[] =
1347 This is a big hack to try and fix the rare case of getting stuck in the world
1351 void SV_CheckStuck (prvm_edict_t *ent)
1356 if (!SV_TestEntityPosition(ent, vec3_origin))
1358 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1362 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1364 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1366 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]);
1367 SV_LinkEdict (ent, true);
1372 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1373 if (!SV_TestEntityPosition(ent, offset))
1375 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1376 SV_LinkEdict (ent, true);
1380 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1383 static void SV_UnstickEntity (prvm_edict_t *ent)
1387 // if not stuck in a bmodel, just return
1388 if (!SV_TestEntityPosition(ent, vec3_origin))
1391 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1393 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1395 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]);
1396 SV_LinkEdict (ent, true);
1401 if (developer.integer >= 100)
1402 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1411 qboolean SV_CheckWater (prvm_edict_t *ent)
1414 int nNativeContents;
1417 point[0] = ent->fields.server->origin[0];
1418 point[1] = ent->fields.server->origin[1];
1419 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1421 // DRESK - Support for Entity Contents Transition Event
1422 // NOTE: Some logic needed to be slightly re-ordered
1423 // to not affect performance and allow for the feature.
1425 // Acquire Super Contents Prior to Resets
1426 cont = SV_PointSuperContents(point);
1427 // Acquire Native Contents Here
1428 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1430 // DRESK - Support for Entity Contents Transition Event
1431 if(ent->fields.server->watertype)
1432 // Entity did NOT Spawn; Check
1433 SV_CheckContentsTransition(ent, nNativeContents);
1436 ent->fields.server->waterlevel = 0;
1437 ent->fields.server->watertype = CONTENTS_EMPTY;
1438 cont = SV_PointSuperContents(point);
1439 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1441 ent->fields.server->watertype = nNativeContents;
1442 ent->fields.server->waterlevel = 1;
1443 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1444 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1446 ent->fields.server->waterlevel = 2;
1447 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1448 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1449 ent->fields.server->waterlevel = 3;
1453 return ent->fields.server->waterlevel > 1;
1462 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1465 vec3_t forward, into, side;
1467 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1468 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1470 // cut the tangential velocity
1471 i = DotProduct (stepnormal, ent->fields.server->velocity);
1472 VectorScale (stepnormal, i, into);
1473 VectorSubtract (ent->fields.server->velocity, into, side);
1474 ent->fields.server->velocity[0] = side[0] * (1 + d);
1475 ent->fields.server->velocity[1] = side[1] * (1 + d);
1481 =====================
1484 Player has come to a dead stop, possibly due to the problem with limited
1485 float precision at some angle joins in the BSP hull.
1487 Try fixing by pushing one pixel in each direction.
1489 This is a hack, but in the interest of good gameplay...
1490 ======================
1492 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1497 VectorCopy (ent->fields.server->origin, oldorg);
1500 for (i=0 ; i<8 ; i++)
1502 // try pushing a little in an axial direction
1505 case 0: dir[0] = 2; dir[1] = 0; break;
1506 case 1: dir[0] = 0; dir[1] = 2; break;
1507 case 2: dir[0] = -2; dir[1] = 0; break;
1508 case 3: dir[0] = 0; dir[1] = -2; break;
1509 case 4: dir[0] = 2; dir[1] = 2; break;
1510 case 5: dir[0] = -2; dir[1] = 2; break;
1511 case 6: dir[0] = 2; dir[1] = -2; break;
1512 case 7: dir[0] = -2; dir[1] = -2; break;
1515 SV_PushEntity (ent, dir, false);
1517 // retry the original move
1518 ent->fields.server->velocity[0] = oldvel[0];
1519 ent->fields.server->velocity[1] = oldvel[1];
1520 ent->fields.server->velocity[2] = 0;
1521 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1523 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1524 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1526 Con_DPrint("TryUnstick - success.\n");
1530 // go back to the original pos and try again
1531 VectorCopy (oldorg, ent->fields.server->origin);
1535 VectorClear (ent->fields.server->velocity);
1536 Con_DPrint("TryUnstick - failure.\n");
1542 =====================
1545 Only used by players
1546 ======================
1548 void SV_WalkMove (prvm_edict_t *ent)
1550 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1551 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1554 // if frametime is 0 (due to client sending the same timestamp twice),
1556 if (sv.frametime <= 0)
1559 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1561 SV_CheckVelocity(ent);
1563 // do a regular slide move unless it looks like you ran into a step
1564 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1566 VectorCopy (ent->fields.server->origin, start_origin);
1567 VectorCopy (ent->fields.server->velocity, start_velocity);
1569 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1571 // if the move did not hit the ground at any point, we're not on ground
1573 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1575 SV_CheckVelocity(ent);
1577 VectorCopy(ent->fields.server->origin, originalmove_origin);
1578 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1579 originalmove_clip = clip;
1580 originalmove_flags = (int)ent->fields.server->flags;
1581 originalmove_groundentity = ent->fields.server->groundentity;
1583 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1586 if (sv_nostep.integer)
1589 // if move didn't block on a step, return
1592 // if move was not trying to move into the step, return
1593 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1596 if (ent->fields.server->movetype != MOVETYPE_FLY)
1598 // return if gibbed by a trigger
1599 if (ent->fields.server->movetype != MOVETYPE_WALK)
1602 // only step up while jumping if that is enabled
1603 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1604 if (!oldonground && ent->fields.server->waterlevel == 0)
1608 // try moving up and forward to go up a step
1609 // back to start pos
1610 VectorCopy (start_origin, ent->fields.server->origin);
1611 VectorCopy (start_velocity, ent->fields.server->velocity);
1614 VectorClear (upmove);
1615 upmove[2] = sv_stepheight.value;
1616 // FIXME: don't link?
1617 SV_PushEntity(ent, upmove, false);
1620 ent->fields.server->velocity[2] = 0;
1621 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1622 ent->fields.server->velocity[2] += start_velocity[2];
1624 SV_CheckVelocity(ent);
1626 // check for stuckness, possibly due to the limited precision of floats
1627 // in the clipping hulls
1629 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1630 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1632 //Con_Printf("wall\n");
1633 // stepping up didn't make any progress, revert to original move
1634 VectorCopy(originalmove_origin, ent->fields.server->origin);
1635 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1636 //clip = originalmove_clip;
1637 ent->fields.server->flags = originalmove_flags;
1638 ent->fields.server->groundentity = originalmove_groundentity;
1639 // now try to unstick if needed
1640 //clip = SV_TryUnstick (ent, oldvel);
1644 //Con_Printf("step - ");
1646 // extra friction based on view angle
1647 if (clip & 2 && sv_wallfriction.integer)
1648 SV_WallFriction (ent, stepnormal);
1650 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1651 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))
1655 VectorClear (downmove);
1656 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1657 // FIXME: don't link?
1658 downtrace = SV_PushEntity (ent, downmove, false);
1660 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1662 // this has been disabled so that you can't jump when you are stepping
1663 // up while already jumping (also known as the Quake2 double jump bug)
1665 // LordHavoc: disabled this check so you can walk on monsters/players
1666 //if (ent->fields.server->solid == SOLID_BSP)
1668 //Con_Printf("onground\n");
1669 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1670 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1676 //Con_Printf("slope\n");
1677 // if the push down didn't end up on good ground, use the move without
1678 // the step up. This happens near wall / slope combinations, and can
1679 // cause the player to hop up higher on a slope too steep to climb
1680 VectorCopy(originalmove_origin, ent->fields.server->origin);
1681 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1682 //clip = originalmove_clip;
1683 ent->fields.server->flags = originalmove_flags;
1684 ent->fields.server->groundentity = originalmove_groundentity;
1687 SV_CheckVelocity(ent);
1690 //============================================================================
1696 Entities that are "stuck" to another entity
1699 void SV_Physics_Follow (prvm_edict_t *ent)
1701 vec3_t vf, vr, vu, angles, v;
1705 if (!SV_RunThink (ent))
1708 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1709 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1710 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])
1712 // quick case for no rotation
1713 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1717 angles[0] = -ent->fields.server->punchangle[0];
1718 angles[1] = ent->fields.server->punchangle[1];
1719 angles[2] = ent->fields.server->punchangle[2];
1720 AngleVectors (angles, vf, vr, vu);
1721 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];
1722 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];
1723 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];
1724 angles[0] = -e->fields.server->angles[0];
1725 angles[1] = e->fields.server->angles[1];
1726 angles[2] = e->fields.server->angles[2];
1727 AngleVectors (angles, vf, vr, vu);
1728 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1729 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1730 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1732 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1733 SV_LinkEdict (ent, true);
1737 ==============================================================================
1741 ==============================================================================
1746 SV_CheckWaterTransition
1750 void SV_CheckWaterTransition (prvm_edict_t *ent)
1753 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1754 if (!ent->fields.server->watertype)
1756 // just spawned here
1757 ent->fields.server->watertype = cont;
1758 ent->fields.server->waterlevel = 1;
1762 // DRESK - Support for Entity Contents Transition Event
1763 // NOTE: Call here BEFORE updating the watertype below,
1764 // and suppress watersplash sound if a valid function
1765 // call was made to allow for custom "splash" sounds.
1766 if( !SV_CheckContentsTransition(ent, cont) )
1767 { // Contents Transition Function Invalid; Potentially Play Water Sound
1768 // check if the entity crossed into or out of water
1769 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1770 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1773 if (cont <= CONTENTS_WATER)
1775 ent->fields.server->watertype = cont;
1776 ent->fields.server->waterlevel = 1;
1780 ent->fields.server->watertype = CONTENTS_EMPTY;
1781 ent->fields.server->waterlevel = 0;
1789 Toss, bounce, and fly movement. When onground, do nothing.
1792 void SV_Physics_Toss (prvm_edict_t *ent)
1797 // if onground, return without moving
1798 if ((int)ent->fields.server->flags & FL_ONGROUND)
1800 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1802 // don't stick to ground if onground and moving upward
1803 ent->fields.server->flags -= FL_ONGROUND;
1805 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1807 // we can trust FL_ONGROUND if groundentity is world because it never moves
1810 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1812 // if ent was supported by a brush model on previous frame,
1813 // and groundentity is now freed, set groundentity to 0 (world)
1814 // which leaves it suspended in the air
1815 ent->fields.server->groundentity = 0;
1819 ent->priv.server->suspendedinairflag = false;
1821 SV_CheckVelocity (ent);
1824 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1825 SV_AddGravity (ent);
1828 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1831 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1832 trace = SV_PushEntity (ent, move, true);
1833 if (ent->priv.server->free)
1835 if (trace.bmodelstartsolid)
1837 // try to unstick the entity
1838 SV_UnstickEntity(ent);
1839 trace = SV_PushEntity (ent, move, false);
1840 if (ent->priv.server->free)
1844 if (trace.fraction < 1)
1846 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1848 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1849 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1851 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1854 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1855 // LordHavoc: fixed grenades not bouncing when fired down a slope
1856 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1858 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1859 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1861 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1862 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1863 VectorClear (ent->fields.server->velocity);
1864 VectorClear (ent->fields.server->avelocity);
1867 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1871 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1873 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1874 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1875 VectorClear (ent->fields.server->velocity);
1876 VectorClear (ent->fields.server->avelocity);
1879 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1884 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1885 if (trace.plane.normal[2] > 0.7)
1887 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1888 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1889 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1890 ent->priv.server->suspendedinairflag = true;
1891 VectorClear (ent->fields.server->velocity);
1892 VectorClear (ent->fields.server->avelocity);
1895 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1899 // check for in water
1900 SV_CheckWaterTransition (ent);
1904 ===============================================================================
1908 ===============================================================================
1915 Monsters freefall when they don't have a ground entity, otherwise
1916 all movement is done with discrete steps.
1918 This is also used for objects that have become still on the ground, but
1919 will fall if the floor is pulled out from under them.
1922 void SV_Physics_Step (prvm_edict_t *ent)
1924 int flags = (int)ent->fields.server->flags;
1925 // don't fall at all if fly/swim
1926 if (!(flags & (FL_FLY | FL_SWIM)))
1928 if (flags & FL_ONGROUND)
1930 // freefall if onground and moving upward
1931 // freefall if not standing on a world surface (it may be a lift or trap door)
1932 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1934 ent->fields.server->flags -= FL_ONGROUND;
1936 SV_CheckVelocity(ent);
1937 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1938 SV_LinkEdict(ent, true);
1943 // freefall if not onground
1944 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1947 SV_CheckVelocity(ent);
1948 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1949 SV_LinkEdict(ent, true);
1952 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1953 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1960 SV_CheckWaterTransition(ent);
1963 //============================================================================
1965 static void SV_Physics_Entity (prvm_edict_t *ent)
1967 // don't run a move on newly spawned projectiles as it messes up movement
1968 // interpolation and rocket trails
1969 qboolean runmove = ent->priv.server->move;
1970 ent->priv.server->move = true;
1971 switch ((int) ent->fields.server->movetype)
1974 case MOVETYPE_FAKEPUSH:
1975 SV_Physics_Pusher (ent);
1978 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1979 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1982 case MOVETYPE_FOLLOW:
1983 SV_Physics_Follow (ent);
1985 case MOVETYPE_NOCLIP:
1986 if (SV_RunThink(ent))
1989 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1990 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1992 SV_LinkEdict(ent, false);
1995 SV_Physics_Step (ent);
1998 if (SV_RunThink (ent))
2000 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2001 SV_AddGravity (ent);
2002 SV_CheckStuck (ent);
2004 SV_LinkEdict (ent, true);
2008 case MOVETYPE_BOUNCE:
2009 case MOVETYPE_BOUNCEMISSILE:
2010 case MOVETYPE_FLYMISSILE:
2013 if (SV_RunThink (ent) && runmove)
2014 SV_Physics_Toss (ent);
2017 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2022 void SV_Physics_ClientMove(void)
2025 ent = host_client->edict;
2027 // call player physics, this needs the proper frametime
2028 prog->globals.server->frametime = sv.frametime;
2031 // call standard client pre-think, with frametime = 0
2032 prog->globals.server->time = sv.time;
2033 prog->globals.server->frametime = 0;
2034 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2035 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2036 prog->globals.server->frametime = sv.frametime;
2038 // make sure the velocity is sane (not a NaN)
2039 SV_CheckVelocity(ent);
2040 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2041 // player_run/player_stand1 does not horribly malfunction if the
2042 // velocity becomes a number that is both == 0 and != 0
2043 // (sounds to me like NaN but to be absolutely safe...)
2044 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2045 VectorClear(ent->fields.server->velocity);
2047 // perform MOVETYPE_WALK behavior
2048 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2049 SV_AddGravity (ent);
2050 SV_CheckStuck (ent);
2053 SV_CheckVelocity (ent);
2055 SV_LinkEdict (ent, true);
2057 SV_CheckVelocity (ent);
2059 // call standard player post-think, with frametime = 0
2060 prog->globals.server->time = sv.time;
2061 prog->globals.server->frametime = 0;
2062 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2063 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2064 prog->globals.server->frametime = sv.frametime;
2066 if(ent->fields.server->fixangle)
2068 // angle fixing was requested by physics code...
2069 // so store the current angles for later use
2070 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2071 host_client->fixangle_angles_set = TRUE;
2073 // and clear fixangle for the next frame
2074 ent->fields.server->fixangle = 0;
2078 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2080 // don't do physics on disconnected clients, FrikBot relies on this
2081 if (!host_client->spawned)
2083 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2087 // don't run physics here if running asynchronously
2088 if (host_client->clmovement_skipphysicsframes <= 0)
2091 // make sure the velocity is sane (not a NaN)
2092 SV_CheckVelocity(ent);
2093 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2094 // player_run/player_stand1 does not horribly malfunction if the
2095 // velocity becomes a number that is both == 0 and != 0
2096 // (sounds to me like NaN but to be absolutely safe...)
2097 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2098 VectorClear(ent->fields.server->velocity);
2100 // call standard client pre-think
2101 prog->globals.server->time = sv.time;
2102 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2103 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2104 SV_CheckVelocity (ent);
2106 switch ((int) ent->fields.server->movetype)
2109 case MOVETYPE_FAKEPUSH:
2110 SV_Physics_Pusher (ent);
2113 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2114 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2117 case MOVETYPE_FOLLOW:
2118 SV_Physics_Follow (ent);
2120 case MOVETYPE_NOCLIP:
2123 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2124 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2127 SV_Physics_Step (ent);
2131 // don't run physics here if running asynchronously
2132 if (host_client->clmovement_skipphysicsframes <= 0)
2134 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2135 SV_AddGravity (ent);
2136 SV_CheckStuck (ent);
2141 case MOVETYPE_BOUNCE:
2142 case MOVETYPE_BOUNCEMISSILE:
2143 case MOVETYPE_FLYMISSILE:
2146 SV_Physics_Toss (ent);
2150 SV_CheckWater (ent);
2154 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2158 // decrement the countdown variable used to decide when to go back to
2159 // synchronous physics
2160 if (host_client->clmovement_skipphysicsframes > 0)
2161 host_client->clmovement_skipphysicsframes--;
2163 SV_CheckVelocity (ent);
2165 SV_LinkEdict (ent, true);
2167 SV_CheckVelocity (ent);
2169 // call standard player post-think
2170 prog->globals.server->time = sv.time;
2171 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2172 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2174 if(ent->fields.server->fixangle)
2176 // angle fixing was requested by physics code...
2177 // so store the current angles for later use
2178 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2179 host_client->fixangle_angles_set = TRUE;
2181 // and clear fixangle for the next frame
2182 ent->fields.server->fixangle = 0;
2192 void SV_Physics (void)
2197 // let the progs know that a new frame has started
2198 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2199 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2200 prog->globals.server->time = sv.time;
2201 prog->globals.server->frametime = sv.frametime;
2202 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2205 // treat each object in turn
2208 // if force_retouch, relink all the entities
2209 if (prog->globals.server->force_retouch > 0)
2210 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2211 if (!ent->priv.server->free)
2212 SV_LinkEdict (ent, true); // force retouch even for stationary
2214 // run physics on the client entities
2215 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2216 if (!ent->priv.server->free)
2217 SV_Physics_ClientEntity(ent);
2219 // run physics on all the non-client entities
2220 if (!sv_freezenonclients.integer)
2221 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2222 if (!ent->priv.server->free)
2223 SV_Physics_Entity(ent);
2225 if (prog->globals.server->force_retouch > 0)
2226 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2228 // LordHavoc: endframe support
2229 if (prog->funcoffsets.EndFrame)
2231 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2232 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2233 prog->globals.server->time = sv.time;
2234 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2237 // decrement prog->num_edicts if the highest number entities died
2238 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2240 if (!sv_freezenonclients.integer)
2241 sv.time += sv.frametime;