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 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;
1032 unsigned short moved_edicts[MAX_EDICTS];
1034 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])
1036 pusher->fields.server->ltime += movetime;
1040 switch ((int) pusher->fields.server->solid)
1042 // LordHavoc: valid pusher types
1045 case SOLID_SLIDEBOX:
1046 case SOLID_CORPSE: // LordHavoc: this would be weird...
1048 // LordHavoc: no collisions
1051 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1052 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1053 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1054 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1055 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1056 pusher->fields.server->ltime += movetime;
1057 SV_LinkEdict (pusher, false);
1060 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1063 index = (int) pusher->fields.server->modelindex;
1064 if (index < 1 || index >= MAX_MODELS)
1066 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1069 pushermodel = sv.models[index];
1071 movetime2 = movetime;
1072 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1073 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1074 if (moveangle[0] || moveangle[2])
1076 for (i = 0;i < 3;i++)
1080 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1081 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1085 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1086 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1090 else if (moveangle[1])
1092 for (i = 0;i < 3;i++)
1096 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1097 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1101 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1102 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1108 for (i = 0;i < 3;i++)
1112 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1113 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1117 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1118 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1123 VectorNegate (moveangle, a);
1124 AngleVectorsFLU (a, forward, left, up);
1126 VectorCopy (pusher->fields.server->origin, pushorig);
1127 VectorCopy (pusher->fields.server->angles, pushang);
1128 pushltime = pusher->fields.server->ltime;
1130 // move the pusher to its final position
1132 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1133 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1134 pusher->fields.server->ltime += movetime;
1135 SV_LinkEdict (pusher, false);
1138 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1139 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1140 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);
1141 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1143 savesolid = pusher->fields.server->solid;
1145 // see if any solid entities are inside the final position
1148 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1149 for (e = 0;e < numcheckentities;e++)
1151 prvm_edict_t *check = checkentities[e];
1152 int checkcontents = SV_GenericHitSuperContentsMask(check);
1153 if (check->fields.server->movetype == MOVETYPE_NONE
1154 || check->fields.server->movetype == MOVETYPE_PUSH
1155 || check->fields.server->movetype == MOVETYPE_FOLLOW
1156 || check->fields.server->movetype == MOVETYPE_NOCLIP
1157 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1160 // if the entity is standing on the pusher, it will definitely be moved
1161 // if the entity is not standing on the pusher, but is in the pusher's
1162 // final position, move it
1163 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1165 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);
1166 if (!trace.startsolid)
1171 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1174 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1175 org2[0] = DotProduct (org, forward);
1176 org2[1] = DotProduct (org, left);
1177 org2[2] = DotProduct (org, up);
1178 VectorSubtract (org2, org, move);
1179 VectorAdd (move, move1, move);
1182 VectorCopy (move1, move);
1184 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1185 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1186 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1188 // try moving the contacted entity
1189 pusher->fields.server->solid = SOLID_NOT;
1190 trace = SV_PushEntity (check, move, true);
1191 // FIXME: turn players specially
1192 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1193 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1194 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1196 // this trace.fraction < 1 check causes items to fall off of pushers
1197 // if they pass under or through a wall
1198 // the groundentity check causes items to fall off of ledges
1199 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1200 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1202 // if it is still inside the pusher, block
1203 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);
1204 if (trace.startsolid)
1206 // try moving the contacted entity a tiny bit further to account for precision errors
1208 pusher->fields.server->solid = SOLID_NOT;
1209 VectorScale(move, 1.1, move2);
1210 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1211 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1212 SV_PushEntity (check, move2, true);
1213 pusher->fields.server->solid = savesolid;
1214 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1215 if (trace.startsolid)
1217 // try moving the contacted entity a tiny bit less to account for precision errors
1218 pusher->fields.server->solid = SOLID_NOT;
1219 VectorScale(move, 0.9, move2);
1220 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1221 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1222 SV_PushEntity (check, move2, true);
1223 pusher->fields.server->solid = savesolid;
1224 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);
1225 if (trace.startsolid)
1227 // still inside pusher, so it's really blocked
1230 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1232 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1235 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1236 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1240 VectorCopy (pushorig, pusher->fields.server->origin);
1241 VectorCopy (pushang, pusher->fields.server->angles);
1242 pusher->fields.server->ltime = pushltime;
1243 SV_LinkEdict (pusher, false);
1245 // move back any entities we already moved
1246 for (i = 0;i < num_moved;i++)
1248 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1249 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1250 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1251 SV_LinkEdict (ed, false);
1254 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1255 if (pusher->fields.server->blocked)
1257 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1258 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1259 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1266 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1267 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1268 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1277 void SV_Physics_Pusher (prvm_edict_t *ent)
1279 float thinktime, oldltime, movetime;
1281 oldltime = ent->fields.server->ltime;
1283 thinktime = ent->fields.server->nextthink;
1284 if (thinktime < ent->fields.server->ltime + sv.frametime)
1286 movetime = thinktime - ent->fields.server->ltime;
1291 movetime = sv.frametime;
1294 // advances ent->fields.server->ltime if not blocked
1295 SV_PushMove (ent, movetime);
1297 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1299 ent->fields.server->nextthink = 0;
1300 prog->globals.server->time = sv.time;
1301 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1302 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1303 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1309 ===============================================================================
1313 ===============================================================================
1316 static float unstickoffsets[] =
1350 This is a big hack to try and fix the rare case of getting stuck in the world
1354 void SV_CheckStuck (prvm_edict_t *ent)
1359 if (!SV_TestEntityPosition(ent, vec3_origin))
1361 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1365 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1367 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1369 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]);
1370 SV_LinkEdict (ent, true);
1375 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1376 if (!SV_TestEntityPosition(ent, offset))
1378 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1379 SV_LinkEdict (ent, true);
1383 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1386 static void SV_UnstickEntity (prvm_edict_t *ent)
1390 // if not stuck in a bmodel, just return
1391 if (!SV_TestEntityPosition(ent, vec3_origin))
1394 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1396 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1398 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]);
1399 SV_LinkEdict (ent, true);
1404 if (developer.integer >= 100)
1405 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1414 qboolean SV_CheckWater (prvm_edict_t *ent)
1417 int nNativeContents;
1420 point[0] = ent->fields.server->origin[0];
1421 point[1] = ent->fields.server->origin[1];
1422 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1424 // DRESK - Support for Entity Contents Transition Event
1425 // NOTE: Some logic needed to be slightly re-ordered
1426 // to not affect performance and allow for the feature.
1428 // Acquire Super Contents Prior to Resets
1429 cont = SV_PointSuperContents(point);
1430 // Acquire Native Contents Here
1431 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1433 // DRESK - Support for Entity Contents Transition Event
1434 if(ent->fields.server->watertype)
1435 // Entity did NOT Spawn; Check
1436 SV_CheckContentsTransition(ent, nNativeContents);
1439 ent->fields.server->waterlevel = 0;
1440 ent->fields.server->watertype = CONTENTS_EMPTY;
1441 cont = SV_PointSuperContents(point);
1442 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1444 ent->fields.server->watertype = nNativeContents;
1445 ent->fields.server->waterlevel = 1;
1446 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1447 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1449 ent->fields.server->waterlevel = 2;
1450 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1451 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1452 ent->fields.server->waterlevel = 3;
1456 return ent->fields.server->waterlevel > 1;
1465 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1468 vec3_t forward, into, side;
1470 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1471 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1473 // cut the tangential velocity
1474 i = DotProduct (stepnormal, ent->fields.server->velocity);
1475 VectorScale (stepnormal, i, into);
1476 VectorSubtract (ent->fields.server->velocity, into, side);
1477 ent->fields.server->velocity[0] = side[0] * (1 + d);
1478 ent->fields.server->velocity[1] = side[1] * (1 + d);
1484 =====================
1487 Player has come to a dead stop, possibly due to the problem with limited
1488 float precision at some angle joins in the BSP hull.
1490 Try fixing by pushing one pixel in each direction.
1492 This is a hack, but in the interest of good gameplay...
1493 ======================
1495 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1500 VectorCopy (ent->fields.server->origin, oldorg);
1503 for (i=0 ; i<8 ; i++)
1505 // try pushing a little in an axial direction
1508 case 0: dir[0] = 2; dir[1] = 0; break;
1509 case 1: dir[0] = 0; dir[1] = 2; break;
1510 case 2: dir[0] = -2; dir[1] = 0; break;
1511 case 3: dir[0] = 0; dir[1] = -2; break;
1512 case 4: dir[0] = 2; dir[1] = 2; break;
1513 case 5: dir[0] = -2; dir[1] = 2; break;
1514 case 6: dir[0] = 2; dir[1] = -2; break;
1515 case 7: dir[0] = -2; dir[1] = -2; break;
1518 SV_PushEntity (ent, dir, false);
1520 // retry the original move
1521 ent->fields.server->velocity[0] = oldvel[0];
1522 ent->fields.server->velocity[1] = oldvel[1];
1523 ent->fields.server->velocity[2] = 0;
1524 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1526 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1527 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1529 Con_DPrint("TryUnstick - success.\n");
1533 // go back to the original pos and try again
1534 VectorCopy (oldorg, ent->fields.server->origin);
1538 VectorClear (ent->fields.server->velocity);
1539 Con_DPrint("TryUnstick - failure.\n");
1545 =====================
1548 Only used by players
1549 ======================
1551 void SV_WalkMove (prvm_edict_t *ent)
1553 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1554 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1557 // if frametime is 0 (due to client sending the same timestamp twice),
1559 if (sv.frametime <= 0)
1562 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1564 SV_CheckVelocity(ent);
1566 // do a regular slide move unless it looks like you ran into a step
1567 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1569 VectorCopy (ent->fields.server->origin, start_origin);
1570 VectorCopy (ent->fields.server->velocity, start_velocity);
1572 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1574 // if the move did not hit the ground at any point, we're not on ground
1576 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1578 SV_CheckVelocity(ent);
1580 VectorCopy(ent->fields.server->origin, originalmove_origin);
1581 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1582 originalmove_clip = clip;
1583 originalmove_flags = (int)ent->fields.server->flags;
1584 originalmove_groundentity = ent->fields.server->groundentity;
1586 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1589 if (sv_nostep.integer)
1592 // if move didn't block on a step, return
1595 // if move was not trying to move into the step, return
1596 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1599 if (ent->fields.server->movetype != MOVETYPE_FLY)
1601 // return if gibbed by a trigger
1602 if (ent->fields.server->movetype != MOVETYPE_WALK)
1605 // only step up while jumping if that is enabled
1606 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1607 if (!oldonground && ent->fields.server->waterlevel == 0)
1611 // try moving up and forward to go up a step
1612 // back to start pos
1613 VectorCopy (start_origin, ent->fields.server->origin);
1614 VectorCopy (start_velocity, ent->fields.server->velocity);
1617 VectorClear (upmove);
1618 upmove[2] = sv_stepheight.value;
1619 // FIXME: don't link?
1620 SV_PushEntity(ent, upmove, false);
1623 ent->fields.server->velocity[2] = 0;
1624 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1625 ent->fields.server->velocity[2] += start_velocity[2];
1627 SV_CheckVelocity(ent);
1629 // check for stuckness, possibly due to the limited precision of floats
1630 // in the clipping hulls
1632 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1633 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1635 //Con_Printf("wall\n");
1636 // stepping up didn't make any progress, revert to original move
1637 VectorCopy(originalmove_origin, ent->fields.server->origin);
1638 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1639 //clip = originalmove_clip;
1640 ent->fields.server->flags = originalmove_flags;
1641 ent->fields.server->groundentity = originalmove_groundentity;
1642 // now try to unstick if needed
1643 //clip = SV_TryUnstick (ent, oldvel);
1647 //Con_Printf("step - ");
1649 // extra friction based on view angle
1650 if (clip & 2 && sv_wallfriction.integer)
1651 SV_WallFriction (ent, stepnormal);
1653 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1654 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))
1658 VectorClear (downmove);
1659 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1660 // FIXME: don't link?
1661 downtrace = SV_PushEntity (ent, downmove, false);
1663 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1665 // this has been disabled so that you can't jump when you are stepping
1666 // up while already jumping (also known as the Quake2 double jump bug)
1668 // LordHavoc: disabled this check so you can walk on monsters/players
1669 //if (ent->fields.server->solid == SOLID_BSP)
1671 //Con_Printf("onground\n");
1672 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1673 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1679 //Con_Printf("slope\n");
1680 // if the push down didn't end up on good ground, use the move without
1681 // the step up. This happens near wall / slope combinations, and can
1682 // cause the player to hop up higher on a slope too steep to climb
1683 VectorCopy(originalmove_origin, ent->fields.server->origin);
1684 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1685 //clip = originalmove_clip;
1686 ent->fields.server->flags = originalmove_flags;
1687 ent->fields.server->groundentity = originalmove_groundentity;
1690 SV_CheckVelocity(ent);
1693 //============================================================================
1699 Entities that are "stuck" to another entity
1702 void SV_Physics_Follow (prvm_edict_t *ent)
1704 vec3_t vf, vr, vu, angles, v;
1708 if (!SV_RunThink (ent))
1711 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1712 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1713 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])
1715 // quick case for no rotation
1716 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1720 angles[0] = -ent->fields.server->punchangle[0];
1721 angles[1] = ent->fields.server->punchangle[1];
1722 angles[2] = ent->fields.server->punchangle[2];
1723 AngleVectors (angles, vf, vr, vu);
1724 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];
1725 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];
1726 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];
1727 angles[0] = -e->fields.server->angles[0];
1728 angles[1] = e->fields.server->angles[1];
1729 angles[2] = e->fields.server->angles[2];
1730 AngleVectors (angles, vf, vr, vu);
1731 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1732 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1733 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1735 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1736 SV_LinkEdict (ent, true);
1740 ==============================================================================
1744 ==============================================================================
1749 SV_CheckWaterTransition
1753 void SV_CheckWaterTransition (prvm_edict_t *ent)
1756 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1757 if (!ent->fields.server->watertype)
1759 // just spawned here
1760 ent->fields.server->watertype = cont;
1761 ent->fields.server->waterlevel = 1;
1765 // DRESK - Support for Entity Contents Transition Event
1766 // NOTE: Call here BEFORE updating the watertype below,
1767 // and suppress watersplash sound if a valid function
1768 // call was made to allow for custom "splash" sounds.
1769 if( !SV_CheckContentsTransition(ent, cont) )
1770 { // Contents Transition Function Invalid; Potentially Play Water Sound
1771 // check if the entity crossed into or out of water
1772 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1773 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1776 if (cont <= CONTENTS_WATER)
1778 ent->fields.server->watertype = cont;
1779 ent->fields.server->waterlevel = 1;
1783 ent->fields.server->watertype = CONTENTS_EMPTY;
1784 ent->fields.server->waterlevel = 0;
1792 Toss, bounce, and fly movement. When onground, do nothing.
1795 void SV_Physics_Toss (prvm_edict_t *ent)
1800 // if onground, return without moving
1801 if ((int)ent->fields.server->flags & FL_ONGROUND)
1803 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1805 // don't stick to ground if onground and moving upward
1806 ent->fields.server->flags -= FL_ONGROUND;
1808 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1810 // we can trust FL_ONGROUND if groundentity is world because it never moves
1813 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1815 // if ent was supported by a brush model on previous frame,
1816 // and groundentity is now freed, set groundentity to 0 (world)
1817 // which leaves it suspended in the air
1818 ent->fields.server->groundentity = 0;
1822 ent->priv.server->suspendedinairflag = false;
1824 SV_CheckVelocity (ent);
1827 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1828 SV_AddGravity (ent);
1831 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1834 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1835 trace = SV_PushEntity (ent, move, true);
1836 if (ent->priv.server->free)
1838 if (trace.bmodelstartsolid)
1840 // try to unstick the entity
1841 SV_UnstickEntity(ent);
1842 trace = SV_PushEntity (ent, move, false);
1843 if (ent->priv.server->free)
1847 if (trace.fraction < 1)
1849 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1851 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1852 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1854 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1857 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1858 // LordHavoc: fixed grenades not bouncing when fired down a slope
1859 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1861 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1862 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1864 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1865 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1866 VectorClear (ent->fields.server->velocity);
1867 VectorClear (ent->fields.server->avelocity);
1870 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1874 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1876 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1877 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1878 VectorClear (ent->fields.server->velocity);
1879 VectorClear (ent->fields.server->avelocity);
1882 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1887 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1888 if (trace.plane.normal[2] > 0.7)
1890 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1891 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1892 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1893 ent->priv.server->suspendedinairflag = true;
1894 VectorClear (ent->fields.server->velocity);
1895 VectorClear (ent->fields.server->avelocity);
1898 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1902 // check for in water
1903 SV_CheckWaterTransition (ent);
1907 ===============================================================================
1911 ===============================================================================
1918 Monsters freefall when they don't have a ground entity, otherwise
1919 all movement is done with discrete steps.
1921 This is also used for objects that have become still on the ground, but
1922 will fall if the floor is pulled out from under them.
1925 void SV_Physics_Step (prvm_edict_t *ent)
1927 int flags = (int)ent->fields.server->flags;
1928 // don't fall at all if fly/swim
1929 if (!(flags & (FL_FLY | FL_SWIM)))
1931 if (flags & FL_ONGROUND)
1933 // freefall if onground and moving upward
1934 // freefall if not standing on a world surface (it may be a lift or trap door)
1935 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1937 ent->fields.server->flags -= FL_ONGROUND;
1939 SV_CheckVelocity(ent);
1940 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1941 SV_LinkEdict(ent, true);
1946 // freefall if not onground
1947 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1950 SV_CheckVelocity(ent);
1951 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1952 SV_LinkEdict(ent, true);
1955 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1956 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1963 SV_CheckWaterTransition(ent);
1966 //============================================================================
1968 static void SV_Physics_Entity (prvm_edict_t *ent)
1970 // don't run a move on newly spawned projectiles as it messes up movement
1971 // interpolation and rocket trails
1972 qboolean runmove = ent->priv.server->move;
1973 ent->priv.server->move = true;
1974 switch ((int) ent->fields.server->movetype)
1977 case MOVETYPE_FAKEPUSH:
1978 SV_Physics_Pusher (ent);
1981 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1982 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1985 case MOVETYPE_FOLLOW:
1986 SV_Physics_Follow (ent);
1988 case MOVETYPE_NOCLIP:
1989 if (SV_RunThink(ent))
1992 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1993 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1995 SV_LinkEdict(ent, false);
1998 SV_Physics_Step (ent);
2001 if (SV_RunThink (ent))
2003 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2004 SV_AddGravity (ent);
2005 SV_CheckStuck (ent);
2007 SV_LinkEdict (ent, true);
2011 case MOVETYPE_BOUNCE:
2012 case MOVETYPE_BOUNCEMISSILE:
2013 case MOVETYPE_FLYMISSILE:
2016 if (SV_RunThink (ent) && runmove)
2017 SV_Physics_Toss (ent);
2020 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2025 void SV_Physics_ClientMove(void)
2028 ent = host_client->edict;
2030 // call player physics, this needs the proper frametime
2031 prog->globals.server->frametime = sv.frametime;
2034 // call standard client pre-think, with frametime = 0
2035 prog->globals.server->time = sv.time;
2036 prog->globals.server->frametime = 0;
2037 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2038 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2039 prog->globals.server->frametime = sv.frametime;
2041 // make sure the velocity is sane (not a NaN)
2042 SV_CheckVelocity(ent);
2043 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2044 // player_run/player_stand1 does not horribly malfunction if the
2045 // velocity becomes a number that is both == 0 and != 0
2046 // (sounds to me like NaN but to be absolutely safe...)
2047 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2048 VectorClear(ent->fields.server->velocity);
2050 // perform MOVETYPE_WALK behavior
2051 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2052 SV_AddGravity (ent);
2053 SV_CheckStuck (ent);
2056 SV_CheckVelocity (ent);
2058 SV_LinkEdict (ent, true);
2060 SV_CheckVelocity (ent);
2062 // call standard player post-think, with frametime = 0
2063 prog->globals.server->time = sv.time;
2064 prog->globals.server->frametime = 0;
2065 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2066 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2067 prog->globals.server->frametime = sv.frametime;
2069 if(ent->fields.server->fixangle)
2071 // angle fixing was requested by physics code...
2072 // so store the current angles for later use
2073 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2074 host_client->fixangle_angles_set = TRUE;
2076 // and clear fixangle for the next frame
2077 ent->fields.server->fixangle = 0;
2081 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2083 // don't do physics on disconnected clients, FrikBot relies on this
2084 if (!host_client->spawned)
2086 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2090 // don't run physics here if running asynchronously
2091 if (host_client->clmovement_skipphysicsframes <= 0)
2094 // make sure the velocity is sane (not a NaN)
2095 SV_CheckVelocity(ent);
2096 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2097 // player_run/player_stand1 does not horribly malfunction if the
2098 // velocity becomes a number that is both == 0 and != 0
2099 // (sounds to me like NaN but to be absolutely safe...)
2100 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2101 VectorClear(ent->fields.server->velocity);
2103 // call standard client pre-think
2104 prog->globals.server->time = sv.time;
2105 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2106 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2107 SV_CheckVelocity (ent);
2109 switch ((int) ent->fields.server->movetype)
2112 case MOVETYPE_FAKEPUSH:
2113 SV_Physics_Pusher (ent);
2116 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2117 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2120 case MOVETYPE_FOLLOW:
2121 SV_Physics_Follow (ent);
2123 case MOVETYPE_NOCLIP:
2126 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2127 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2130 SV_Physics_Step (ent);
2134 // don't run physics here if running asynchronously
2135 if (host_client->clmovement_skipphysicsframes <= 0)
2137 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2138 SV_AddGravity (ent);
2139 SV_CheckStuck (ent);
2144 case MOVETYPE_BOUNCE:
2145 case MOVETYPE_BOUNCEMISSILE:
2146 case MOVETYPE_FLYMISSILE:
2149 SV_Physics_Toss (ent);
2153 SV_CheckWater (ent);
2157 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2161 // decrement the countdown variable used to decide when to go back to
2162 // synchronous physics
2163 if (host_client->clmovement_skipphysicsframes > 0)
2164 host_client->clmovement_skipphysicsframes--;
2166 SV_CheckVelocity (ent);
2168 SV_LinkEdict (ent, true);
2170 SV_CheckVelocity (ent);
2172 // call standard player post-think
2173 prog->globals.server->time = sv.time;
2174 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2175 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2177 if(ent->fields.server->fixangle)
2179 // angle fixing was requested by physics code...
2180 // so store the current angles for later use
2181 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2182 host_client->fixangle_angles_set = TRUE;
2184 // and clear fixangle for the next frame
2185 ent->fields.server->fixangle = 0;
2195 void SV_Physics (void)
2200 // let the progs know that a new frame has started
2201 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2202 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2203 prog->globals.server->time = sv.time;
2204 prog->globals.server->frametime = sv.frametime;
2205 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2208 // treat each object in turn
2211 // if force_retouch, relink all the entities
2212 if (prog->globals.server->force_retouch > 0)
2213 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2214 if (!ent->priv.server->free)
2215 SV_LinkEdict (ent, true); // force retouch even for stationary
2217 // run physics on the client entities
2218 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2219 if (!ent->priv.server->free)
2220 SV_Physics_ClientEntity(ent);
2222 // run physics on all the non-client entities
2223 if (!sv_freezenonclients.integer)
2224 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2225 if (!ent->priv.server->free)
2226 SV_Physics_Entity(ent);
2228 if (prog->globals.server->force_retouch > 0)
2229 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2231 // LordHavoc: endframe support
2232 if (prog->funcoffsets.EndFrame)
2234 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2235 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2236 prog->globals.server->time = sv.time;
2237 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2240 // decrement prog->num_edicts if the highest number entities died
2241 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2243 if (!sv_freezenonclients.integer)
2244 sv.time += sv.frametime;