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 trace.fraction < 1 check causes items to fall off of pushers
1195 // if they pass under or through a wall
1196 // the groundentity check causes items to fall off of ledges
1197 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1198 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1200 // if it is still inside the pusher, block
1201 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);
1202 if (trace.startsolid)
1204 // try moving the contacted entity a tiny bit further to account for precision errors
1206 pusher->fields.server->solid = SOLID_NOT;
1207 VectorScale(move, 1.1, move2);
1208 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1209 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1210 SV_PushEntity (check, move2, true);
1211 pusher->fields.server->solid = savesolid;
1212 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);
1213 if (trace.startsolid)
1215 // try moving the contacted entity a tiny bit less to account for precision errors
1216 pusher->fields.server->solid = SOLID_NOT;
1217 VectorScale(move, 0.9, move2);
1218 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1219 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1220 SV_PushEntity (check, move2, true);
1221 pusher->fields.server->solid = savesolid;
1222 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);
1223 if (trace.startsolid)
1225 // still inside pusher, so it's really blocked
1228 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1230 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1233 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1234 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1238 VectorCopy (pushorig, pusher->fields.server->origin);
1239 VectorCopy (pushang, pusher->fields.server->angles);
1240 pusher->fields.server->ltime = pushltime;
1241 SV_LinkEdict (pusher, false);
1243 // move back any entities we already moved
1244 for (i = 0;i < num_moved;i++)
1246 prvm_edict_t *ed = sv.moved_edicts[i];
1247 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1248 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1249 SV_LinkEdict (ed, false);
1252 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1253 if (pusher->fields.server->blocked)
1255 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1256 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1257 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1264 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1265 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1266 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1275 void SV_Physics_Pusher (prvm_edict_t *ent)
1277 float thinktime, oldltime, movetime;
1279 oldltime = ent->fields.server->ltime;
1281 thinktime = ent->fields.server->nextthink;
1282 if (thinktime < ent->fields.server->ltime + sv.frametime)
1284 movetime = thinktime - ent->fields.server->ltime;
1289 movetime = sv.frametime;
1292 // advances ent->fields.server->ltime if not blocked
1293 SV_PushMove (ent, movetime);
1295 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1297 ent->fields.server->nextthink = 0;
1298 prog->globals.server->time = sv.time;
1299 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1300 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1301 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1307 ===============================================================================
1311 ===============================================================================
1314 static float unstickoffsets[] =
1348 This is a big hack to try and fix the rare case of getting stuck in the world
1352 void SV_CheckStuck (prvm_edict_t *ent)
1357 if (!SV_TestEntityPosition(ent, vec3_origin))
1359 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1363 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1365 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1367 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]);
1368 SV_LinkEdict (ent, true);
1373 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1374 if (!SV_TestEntityPosition(ent, offset))
1376 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1377 SV_LinkEdict (ent, true);
1381 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1384 static void SV_UnstickEntity (prvm_edict_t *ent)
1388 // if not stuck in a bmodel, just return
1389 if (!SV_TestEntityPosition(ent, vec3_origin))
1392 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1394 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1396 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]);
1397 SV_LinkEdict (ent, true);
1402 if (developer.integer >= 100)
1403 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1412 qboolean SV_CheckWater (prvm_edict_t *ent)
1415 int nNativeContents;
1418 point[0] = ent->fields.server->origin[0];
1419 point[1] = ent->fields.server->origin[1];
1420 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1422 // DRESK - Support for Entity Contents Transition Event
1423 // NOTE: Some logic needed to be slightly re-ordered
1424 // to not affect performance and allow for the feature.
1426 // Acquire Super Contents Prior to Resets
1427 cont = SV_PointSuperContents(point);
1428 // Acquire Native Contents Here
1429 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1431 // DRESK - Support for Entity Contents Transition Event
1432 if(ent->fields.server->watertype)
1433 // Entity did NOT Spawn; Check
1434 SV_CheckContentsTransition(ent, nNativeContents);
1437 ent->fields.server->waterlevel = 0;
1438 ent->fields.server->watertype = CONTENTS_EMPTY;
1439 cont = SV_PointSuperContents(point);
1440 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1442 ent->fields.server->watertype = nNativeContents;
1443 ent->fields.server->waterlevel = 1;
1444 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1445 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1447 ent->fields.server->waterlevel = 2;
1448 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1449 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1450 ent->fields.server->waterlevel = 3;
1454 return ent->fields.server->waterlevel > 1;
1463 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1466 vec3_t forward, into, side;
1468 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1469 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1471 // cut the tangential velocity
1472 i = DotProduct (stepnormal, ent->fields.server->velocity);
1473 VectorScale (stepnormal, i, into);
1474 VectorSubtract (ent->fields.server->velocity, into, side);
1475 ent->fields.server->velocity[0] = side[0] * (1 + d);
1476 ent->fields.server->velocity[1] = side[1] * (1 + d);
1482 =====================
1485 Player has come to a dead stop, possibly due to the problem with limited
1486 float precision at some angle joins in the BSP hull.
1488 Try fixing by pushing one pixel in each direction.
1490 This is a hack, but in the interest of good gameplay...
1491 ======================
1493 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1498 VectorCopy (ent->fields.server->origin, oldorg);
1501 for (i=0 ; i<8 ; i++)
1503 // try pushing a little in an axial direction
1506 case 0: dir[0] = 2; dir[1] = 0; break;
1507 case 1: dir[0] = 0; dir[1] = 2; break;
1508 case 2: dir[0] = -2; dir[1] = 0; break;
1509 case 3: dir[0] = 0; dir[1] = -2; break;
1510 case 4: dir[0] = 2; dir[1] = 2; break;
1511 case 5: dir[0] = -2; dir[1] = 2; break;
1512 case 6: dir[0] = 2; dir[1] = -2; break;
1513 case 7: dir[0] = -2; dir[1] = -2; break;
1516 SV_PushEntity (ent, dir, false);
1518 // retry the original move
1519 ent->fields.server->velocity[0] = oldvel[0];
1520 ent->fields.server->velocity[1] = oldvel[1];
1521 ent->fields.server->velocity[2] = 0;
1522 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1524 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1525 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1527 Con_DPrint("TryUnstick - success.\n");
1531 // go back to the original pos and try again
1532 VectorCopy (oldorg, ent->fields.server->origin);
1536 VectorClear (ent->fields.server->velocity);
1537 Con_DPrint("TryUnstick - failure.\n");
1543 =====================
1546 Only used by players
1547 ======================
1549 void SV_WalkMove (prvm_edict_t *ent)
1551 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1552 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1555 // if frametime is 0 (due to client sending the same timestamp twice),
1557 if (sv.frametime <= 0)
1560 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1562 SV_CheckVelocity(ent);
1564 // do a regular slide move unless it looks like you ran into a step
1565 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1567 VectorCopy (ent->fields.server->origin, start_origin);
1568 VectorCopy (ent->fields.server->velocity, start_velocity);
1570 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1572 // if the move did not hit the ground at any point, we're not on ground
1574 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1576 SV_CheckVelocity(ent);
1578 VectorCopy(ent->fields.server->origin, originalmove_origin);
1579 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1580 originalmove_clip = clip;
1581 originalmove_flags = (int)ent->fields.server->flags;
1582 originalmove_groundentity = ent->fields.server->groundentity;
1584 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1587 if (sv_nostep.integer)
1590 // if move didn't block on a step, return
1593 // if move was not trying to move into the step, return
1594 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1597 if (ent->fields.server->movetype != MOVETYPE_FLY)
1599 // return if gibbed by a trigger
1600 if (ent->fields.server->movetype != MOVETYPE_WALK)
1603 // only step up while jumping if that is enabled
1604 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1605 if (!oldonground && ent->fields.server->waterlevel == 0)
1609 // try moving up and forward to go up a step
1610 // back to start pos
1611 VectorCopy (start_origin, ent->fields.server->origin);
1612 VectorCopy (start_velocity, ent->fields.server->velocity);
1615 VectorClear (upmove);
1616 upmove[2] = sv_stepheight.value;
1617 // FIXME: don't link?
1618 SV_PushEntity(ent, upmove, false);
1621 ent->fields.server->velocity[2] = 0;
1622 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1623 ent->fields.server->velocity[2] += start_velocity[2];
1625 SV_CheckVelocity(ent);
1627 // check for stuckness, possibly due to the limited precision of floats
1628 // in the clipping hulls
1630 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1631 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1633 //Con_Printf("wall\n");
1634 // stepping up didn't make any progress, revert to original move
1635 VectorCopy(originalmove_origin, ent->fields.server->origin);
1636 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1637 //clip = originalmove_clip;
1638 ent->fields.server->flags = originalmove_flags;
1639 ent->fields.server->groundentity = originalmove_groundentity;
1640 // now try to unstick if needed
1641 //clip = SV_TryUnstick (ent, oldvel);
1645 //Con_Printf("step - ");
1647 // extra friction based on view angle
1648 if (clip & 2 && sv_wallfriction.integer)
1649 SV_WallFriction (ent, stepnormal);
1651 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1652 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))
1656 VectorClear (downmove);
1657 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1658 // FIXME: don't link?
1659 downtrace = SV_PushEntity (ent, downmove, false);
1661 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1663 // this has been disabled so that you can't jump when you are stepping
1664 // up while already jumping (also known as the Quake2 double jump bug)
1666 // LordHavoc: disabled this check so you can walk on monsters/players
1667 //if (ent->fields.server->solid == SOLID_BSP)
1669 //Con_Printf("onground\n");
1670 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1671 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1677 //Con_Printf("slope\n");
1678 // if the push down didn't end up on good ground, use the move without
1679 // the step up. This happens near wall / slope combinations, and can
1680 // cause the player to hop up higher on a slope too steep to climb
1681 VectorCopy(originalmove_origin, ent->fields.server->origin);
1682 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1683 //clip = originalmove_clip;
1684 ent->fields.server->flags = originalmove_flags;
1685 ent->fields.server->groundentity = originalmove_groundentity;
1688 SV_CheckVelocity(ent);
1691 //============================================================================
1697 Entities that are "stuck" to another entity
1700 void SV_Physics_Follow (prvm_edict_t *ent)
1702 vec3_t vf, vr, vu, angles, v;
1706 if (!SV_RunThink (ent))
1709 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1710 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1711 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])
1713 // quick case for no rotation
1714 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1718 angles[0] = -ent->fields.server->punchangle[0];
1719 angles[1] = ent->fields.server->punchangle[1];
1720 angles[2] = ent->fields.server->punchangle[2];
1721 AngleVectors (angles, vf, vr, vu);
1722 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];
1723 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];
1724 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];
1725 angles[0] = -e->fields.server->angles[0];
1726 angles[1] = e->fields.server->angles[1];
1727 angles[2] = e->fields.server->angles[2];
1728 AngleVectors (angles, vf, vr, vu);
1729 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1730 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1731 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1733 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1734 SV_LinkEdict (ent, true);
1738 ==============================================================================
1742 ==============================================================================
1747 SV_CheckWaterTransition
1751 void SV_CheckWaterTransition (prvm_edict_t *ent)
1754 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1755 if (!ent->fields.server->watertype)
1757 // just spawned here
1758 ent->fields.server->watertype = cont;
1759 ent->fields.server->waterlevel = 1;
1763 // DRESK - Support for Entity Contents Transition Event
1764 // NOTE: Call here BEFORE updating the watertype below,
1765 // and suppress watersplash sound if a valid function
1766 // call was made to allow for custom "splash" sounds.
1767 if( !SV_CheckContentsTransition(ent, cont) )
1768 { // Contents Transition Function Invalid; Potentially Play Water Sound
1769 // check if the entity crossed into or out of water
1770 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1771 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1774 if (cont <= CONTENTS_WATER)
1776 ent->fields.server->watertype = cont;
1777 ent->fields.server->waterlevel = 1;
1781 ent->fields.server->watertype = CONTENTS_EMPTY;
1782 ent->fields.server->waterlevel = 0;
1790 Toss, bounce, and fly movement. When onground, do nothing.
1793 void SV_Physics_Toss (prvm_edict_t *ent)
1798 // if onground, return without moving
1799 if ((int)ent->fields.server->flags & FL_ONGROUND)
1801 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1803 // don't stick to ground if onground and moving upward
1804 ent->fields.server->flags -= FL_ONGROUND;
1806 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1808 // we can trust FL_ONGROUND if groundentity is world because it never moves
1811 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1813 // if ent was supported by a brush model on previous frame,
1814 // and groundentity is now freed, set groundentity to 0 (world)
1815 // which leaves it suspended in the air
1816 ent->fields.server->groundentity = 0;
1820 ent->priv.server->suspendedinairflag = false;
1822 SV_CheckVelocity (ent);
1825 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1826 SV_AddGravity (ent);
1829 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1832 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1833 trace = SV_PushEntity (ent, move, true);
1834 if (ent->priv.server->free)
1836 if (trace.bmodelstartsolid)
1838 // try to unstick the entity
1839 SV_UnstickEntity(ent);
1840 trace = SV_PushEntity (ent, move, false);
1841 if (ent->priv.server->free)
1845 if (trace.fraction < 1)
1847 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1849 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1850 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1852 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1855 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1856 // LordHavoc: fixed grenades not bouncing when fired down a slope
1857 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1859 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1860 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1862 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1863 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1864 VectorClear (ent->fields.server->velocity);
1865 VectorClear (ent->fields.server->avelocity);
1868 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1872 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1874 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1875 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1876 VectorClear (ent->fields.server->velocity);
1877 VectorClear (ent->fields.server->avelocity);
1880 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1885 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1886 if (trace.plane.normal[2] > 0.7)
1888 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1889 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1890 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1891 ent->priv.server->suspendedinairflag = true;
1892 VectorClear (ent->fields.server->velocity);
1893 VectorClear (ent->fields.server->avelocity);
1896 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1900 // check for in water
1901 SV_CheckWaterTransition (ent);
1905 ===============================================================================
1909 ===============================================================================
1916 Monsters freefall when they don't have a ground entity, otherwise
1917 all movement is done with discrete steps.
1919 This is also used for objects that have become still on the ground, but
1920 will fall if the floor is pulled out from under them.
1923 void SV_Physics_Step (prvm_edict_t *ent)
1925 int flags = (int)ent->fields.server->flags;
1926 // don't fall at all if fly/swim
1927 if (!(flags & (FL_FLY | FL_SWIM)))
1929 if (flags & FL_ONGROUND)
1931 // freefall if onground and moving upward
1932 // freefall if not standing on a world surface (it may be a lift or trap door)
1933 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1935 ent->fields.server->flags -= FL_ONGROUND;
1937 SV_CheckVelocity(ent);
1938 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1939 SV_LinkEdict(ent, true);
1944 // freefall if not onground
1945 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1948 SV_CheckVelocity(ent);
1949 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1950 SV_LinkEdict(ent, true);
1953 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1954 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1961 SV_CheckWaterTransition(ent);
1964 //============================================================================
1966 static void SV_Physics_Entity (prvm_edict_t *ent)
1968 // don't run a move on newly spawned projectiles as it messes up movement
1969 // interpolation and rocket trails
1970 qboolean runmove = ent->priv.server->move;
1971 ent->priv.server->move = true;
1972 switch ((int) ent->fields.server->movetype)
1975 case MOVETYPE_FAKEPUSH:
1976 SV_Physics_Pusher (ent);
1979 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1980 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1983 case MOVETYPE_FOLLOW:
1984 SV_Physics_Follow (ent);
1986 case MOVETYPE_NOCLIP:
1987 if (SV_RunThink(ent))
1990 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1991 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1993 SV_LinkEdict(ent, false);
1996 SV_Physics_Step (ent);
1999 if (SV_RunThink (ent))
2001 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2002 SV_AddGravity (ent);
2003 SV_CheckStuck (ent);
2005 SV_LinkEdict (ent, true);
2009 case MOVETYPE_BOUNCE:
2010 case MOVETYPE_BOUNCEMISSILE:
2011 case MOVETYPE_FLYMISSILE:
2014 if (SV_RunThink (ent) && runmove)
2015 SV_Physics_Toss (ent);
2018 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2023 void SV_Physics_ClientMove(void)
2026 ent = host_client->edict;
2028 // call player physics, this needs the proper frametime
2029 prog->globals.server->frametime = sv.frametime;
2032 // call standard client pre-think, with frametime = 0
2033 prog->globals.server->time = sv.time;
2034 prog->globals.server->frametime = 0;
2035 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2036 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2037 prog->globals.server->frametime = sv.frametime;
2039 // make sure the velocity is sane (not a NaN)
2040 SV_CheckVelocity(ent);
2041 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2042 // player_run/player_stand1 does not horribly malfunction if the
2043 // velocity becomes a number that is both == 0 and != 0
2044 // (sounds to me like NaN but to be absolutely safe...)
2045 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2046 VectorClear(ent->fields.server->velocity);
2048 // perform MOVETYPE_WALK behavior
2049 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2050 SV_AddGravity (ent);
2051 SV_CheckStuck (ent);
2054 SV_CheckVelocity (ent);
2056 SV_LinkEdict (ent, true);
2058 SV_CheckVelocity (ent);
2060 // call standard player post-think, with frametime = 0
2061 prog->globals.server->time = sv.time;
2062 prog->globals.server->frametime = 0;
2063 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2064 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2065 prog->globals.server->frametime = sv.frametime;
2067 if(ent->fields.server->fixangle)
2069 // angle fixing was requested by physics code...
2070 // so store the current angles for later use
2071 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2072 host_client->fixangle_angles_set = TRUE;
2074 // and clear fixangle for the next frame
2075 ent->fields.server->fixangle = 0;
2079 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2081 // don't do physics on disconnected clients, FrikBot relies on this
2082 if (!host_client->spawned)
2084 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2088 // don't run physics here if running asynchronously
2089 if (host_client->clmovement_skipphysicsframes <= 0)
2092 // make sure the velocity is sane (not a NaN)
2093 SV_CheckVelocity(ent);
2094 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2095 // player_run/player_stand1 does not horribly malfunction if the
2096 // velocity becomes a number that is both == 0 and != 0
2097 // (sounds to me like NaN but to be absolutely safe...)
2098 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2099 VectorClear(ent->fields.server->velocity);
2101 // call standard client pre-think
2102 prog->globals.server->time = sv.time;
2103 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2104 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2105 SV_CheckVelocity (ent);
2107 switch ((int) ent->fields.server->movetype)
2110 case MOVETYPE_FAKEPUSH:
2111 SV_Physics_Pusher (ent);
2114 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2115 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2118 case MOVETYPE_FOLLOW:
2119 SV_Physics_Follow (ent);
2121 case MOVETYPE_NOCLIP:
2124 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2125 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2128 SV_Physics_Step (ent);
2132 // don't run physics here if running asynchronously
2133 if (host_client->clmovement_skipphysicsframes <= 0)
2135 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2136 SV_AddGravity (ent);
2137 SV_CheckStuck (ent);
2142 case MOVETYPE_BOUNCE:
2143 case MOVETYPE_BOUNCEMISSILE:
2144 case MOVETYPE_FLYMISSILE:
2147 SV_Physics_Toss (ent);
2151 SV_CheckWater (ent);
2155 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2159 // decrement the countdown variable used to decide when to go back to
2160 // synchronous physics
2161 if (host_client->clmovement_skipphysicsframes > 0)
2162 host_client->clmovement_skipphysicsframes--;
2164 SV_CheckVelocity (ent);
2166 SV_LinkEdict (ent, true);
2168 SV_CheckVelocity (ent);
2170 // call standard player post-think
2171 prog->globals.server->time = sv.time;
2172 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2173 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2175 if(ent->fields.server->fixangle)
2177 // angle fixing was requested by physics code...
2178 // so store the current angles for later use
2179 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2180 host_client->fixangle_angles_set = TRUE;
2182 // and clear fixangle for the next frame
2183 ent->fields.server->fixangle = 0;
2193 void SV_Physics (void)
2198 // let the progs know that a new frame has started
2199 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2200 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2201 prog->globals.server->time = sv.time;
2202 prog->globals.server->frametime = sv.frametime;
2203 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2206 // treat each object in turn
2209 // if force_retouch, relink all the entities
2210 if (prog->globals.server->force_retouch > 0)
2211 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2212 if (!ent->priv.server->free)
2213 SV_LinkEdict (ent, true); // force retouch even for stationary
2215 // run physics on the client entities
2216 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2217 if (!ent->priv.server->free)
2218 SV_Physics_ClientEntity(ent);
2220 // run physics on all the non-client entities
2221 if (!sv_freezenonclients.integer)
2222 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2223 if (!ent->priv.server->free)
2224 SV_Physics_Entity(ent);
2226 if (prog->globals.server->force_retouch > 0)
2227 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2229 // LordHavoc: endframe support
2230 if (prog->funcoffsets.EndFrame)
2232 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2233 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2234 prog->globals.server->time = sv.time;
2235 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2238 // decrement prog->num_edicts if the highest number entities died
2239 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2241 if (!sv_freezenonclients.integer)
2242 sv.time += sv.frametime;