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 // Get Allow Touch with Owner
150 int nAllowTouchWithOwner = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.allowtouchwithowner)->_int;
152 VectorCopy(start, clipstart);
153 VectorCopy(end, clipend);
154 VectorCopy(mins, clipmins);
155 VectorCopy(maxs, clipmaxs);
156 VectorCopy(mins, clipmins2);
157 VectorCopy(maxs, clipmaxs2);
158 #if COLLISIONPARANOID >= 3
159 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
163 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
164 cliptrace.bmodelstartsolid = cliptrace.startsolid;
165 if (cliptrace.startsolid || cliptrace.fraction < 1)
166 cliptrace.ent = prog->edicts;
167 if (type == MOVE_WORLDONLY)
170 if (type == MOVE_MISSILE)
172 // LordHavoc: modified this, was = -15, now -= 15
173 for (i = 0;i < 3;i++)
180 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
181 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
182 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
185 VectorCopy(clipmins, hullmins);
186 VectorCopy(clipmaxs, hullmaxs);
189 // create the bounding box of the entire move
190 for (i = 0;i < 3;i++)
192 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
193 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
196 // debug override to test against everything
197 if (sv_debugmove.integer)
199 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
200 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
203 // if the passedict is world, make it NULL (to avoid two checks each time)
204 if (passedict == prog->edicts)
206 // precalculate prog value for passedict for comparisons
207 passedictprog = PRVM_EDICT_TO_PROG(passedict);
208 // figure out whether this is a point trace for comparisons
209 pointtrace = VectorCompare(clipmins, clipmaxs);
210 // precalculate passedict's owner edict pointer for comparisons
211 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
214 // because this uses World_EntitiestoBox, we know all entity boxes overlap
215 // the clip region, so we can skip culling checks in the loop below
216 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
217 if (numtouchedicts > MAX_EDICTS)
219 // this never happens
220 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
221 numtouchedicts = MAX_EDICTS;
223 for (i = 0;i < numtouchedicts;i++)
225 touch = touchedicts[i];
227 if (touch->fields.server->solid < SOLID_BBOX)
229 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
234 // don't clip against self
235 if (passedict == touch)
237 // don't clip owned entities against owner
238 if (!nAllowTouchWithOwner && traceowner == touch)
240 // don't clip owner against owned entities
241 if (!nAllowTouchWithOwner && passedictprog == touch->fields.server->owner)
243 // don't clip points against points (they can't collide)
244 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
248 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
250 // might interact, so do an exact clip
252 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
254 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
255 // if the modelindex is 0, it shouldn't be SOLID_BSP!
256 if (modelindex > 0 && modelindex < MAX_MODELS)
257 model = sv.models[(int)touch->fields.server->modelindex];
260 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);
262 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
263 Matrix4x4_Invert_Simple(&imatrix, &matrix);
264 if ((int)touch->fields.server->flags & FL_MONSTER)
265 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
267 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
269 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
275 #if COLLISIONPARANOID >= 1
276 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)
281 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
284 VectorCopy(trace.endpos, temp);
285 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
286 #if COLLISIONPARANOID < 3
287 if (trace.startsolid || endstuck)
289 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" : "");
296 ===============================================================================
298 Linking entities into the world culling system
300 ===============================================================================
303 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
305 int i, numtouchedicts, old_self, old_other;
306 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
308 // build a list of edicts to touch, because the link loop can be corrupted
309 // by SV_IncreaseEdicts called during touch functions
310 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
311 if (numtouchedicts > MAX_EDICTS)
313 // this never happens
314 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
315 numtouchedicts = MAX_EDICTS;
318 old_self = prog->globals.server->self;
319 old_other = prog->globals.server->other;
320 for (i = 0;i < numtouchedicts;i++)
322 touch = touchedicts[i];
323 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
326 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
327 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
328 prog->globals.server->time = sv.time;
329 prog->globals.server->trace_allsolid = false;
330 prog->globals.server->trace_startsolid = false;
331 prog->globals.server->trace_fraction = 1;
332 prog->globals.server->trace_inwater = false;
333 prog->globals.server->trace_inopen = true;
334 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
335 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
336 prog->globals.server->trace_plane_dist = 0;
337 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
338 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
340 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
342 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
344 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
346 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
349 prog->globals.server->self = old_self;
350 prog->globals.server->other = old_other;
359 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
364 if (ent == prog->edicts)
365 return; // don't add the world
367 if (ent->priv.server->free)
372 if (ent->fields.server->solid == SOLID_BSP)
374 int modelindex = (int)ent->fields.server->modelindex;
375 if (modelindex < 0 || modelindex > MAX_MODELS)
377 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
380 model = sv.models[modelindex];
383 if (!model->TraceBox)
384 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
386 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
388 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
389 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
391 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
393 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
394 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
398 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
399 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
404 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
405 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
406 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
411 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
412 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
416 // to make items easier to pick up and allow them to be grabbed off
417 // of shelves, the abs sizes are expanded
419 if ((int)ent->fields.server->flags & FL_ITEM)
430 // because movement is clipped an epsilon away from an actual edge,
431 // we must fully check even when bounding boxes don't quite touch
440 VectorCopy(mins, ent->fields.server->absmin);
441 VectorCopy(maxs, ent->fields.server->absmax);
443 World_LinkEdict(&sv.world, ent, mins, maxs);
445 // if touch_triggers, call touch on all entities overlapping this box
446 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
447 SV_LinkEdict_TouchAreaGrid(ent);
451 ===============================================================================
455 ===============================================================================
460 SV_TestEntityPosition
462 returns true if the entity is in solid currently
465 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
469 VectorAdd(ent->fields.server->origin, offset, org);
470 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
471 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
475 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
477 // q1bsp/hlbsp use hulls and if the entity does not exactly match
478 // a hull size it is incorrectly tested, so this code tries to
479 // 'fix' it slightly...
480 // FIXME: this breaks entities larger than the hull size
483 VectorAdd(org, ent->fields.server->mins, m1);
484 VectorAdd(org, ent->fields.server->maxs, m2);
485 VectorSubtract(m2, m1, s);
486 #define EPSILON (1.0f / 32.0f)
487 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
488 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
489 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
490 for (i = 0;i < 8;i++)
492 v[0] = (i & 1) ? m2[0] : m1[0];
493 v[1] = (i & 2) ? m2[1] : m1[1];
494 v[2] = (i & 4) ? m2[2] : m1[2];
495 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
500 // if the trace found a better position for the entity, move it there
501 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
502 VectorCopy(trace.endpos, ent->fields.server->origin);
511 void SV_CheckAllEnts (void)
516 // see if any solid entities are inside the final position
517 check = PRVM_NEXT_EDICT(prog->edicts);
518 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
520 if (check->priv.server->free)
522 if (check->fields.server->movetype == MOVETYPE_PUSH
523 || check->fields.server->movetype == MOVETYPE_NONE
524 || check->fields.server->movetype == MOVETYPE_FOLLOW
525 || check->fields.server->movetype == MOVETYPE_NOCLIP)
528 if (SV_TestEntityPosition (check, vec3_origin))
529 Con_Print("entity in invalid position\n");
533 // DRESK - Support for Entity Contents Transition Event
536 SV_CheckContentsTransition
538 returns true if entity had a valid contentstransition function call
541 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
543 int bValidFunctionCall;
544 prvm_eval_t *contentstransition;
546 // Default Valid Function Call to False
547 bValidFunctionCall = false;
549 if(ent->fields.server->watertype != nContents)
550 { // Changed Contents
551 // Acquire Contents Transition Function from QC
552 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
554 if(contentstransition->function)
555 { // Valid Function; Execute
556 // Assign Valid Function
557 bValidFunctionCall = true;
558 // Prepare Parameters (Original Contents, New Contents)
560 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
562 PRVM_G_FLOAT(OFS_PARM1) = nContents;
563 // Execute VM Function
564 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
568 // Return if Function Call was Valid
569 return bValidFunctionCall;
578 void SV_CheckVelocity (prvm_edict_t *ent)
586 for (i=0 ; i<3 ; i++)
588 if (IS_NAN(ent->fields.server->velocity[i]))
590 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
591 ent->fields.server->velocity[i] = 0;
593 if (IS_NAN(ent->fields.server->origin[i]))
595 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
596 ent->fields.server->origin[i] = 0;
600 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
601 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
602 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
604 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
605 ent->fields.server->velocity[0] *= wishspeed;
606 ent->fields.server->velocity[1] *= wishspeed;
607 ent->fields.server->velocity[2] *= wishspeed;
615 Runs thinking code if time. There is some play in the exact time the think
616 function will be called, because it is called before any movement is done
617 in a frame. Not used for pushmove objects, because they must be exact.
618 Returns false if the entity removed itself.
621 qboolean SV_RunThink (prvm_edict_t *ent)
625 thinktime = ent->fields.server->nextthink;
626 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
629 // don't let things stay in the past.
630 // it is possible to start that way by a trigger with a local time.
631 if (thinktime < sv.time)
634 ent->fields.server->nextthink = 0;
635 prog->globals.server->time = thinktime;
636 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
637 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
638 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
639 return !ent->priv.server->free;
646 Two entities have touched, so run their touch functions
649 extern void VM_SetTraceGlobals(const trace_t *trace);
650 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
652 int old_self, old_other;
653 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
656 old_self = prog->globals.server->self;
657 old_other = prog->globals.server->other;
659 VM_SetTraceGlobals(trace);
661 prog->globals.server->time = sv.time;
662 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
664 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
665 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
666 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
669 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
671 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
672 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
673 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
674 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
675 prog->globals.server->trace_plane_dist = -trace->plane.dist;
676 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
677 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
679 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
681 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
683 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
685 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
688 prog->globals.server->self = old_self;
689 prog->globals.server->other = old_other;
697 Slide off of the impacting object
698 returns the blocked flags (1 = floor, 2 = step / wall)
701 #define STOP_EPSILON 0.1
702 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
707 backoff = -DotProduct (in, normal) * overbounce;
708 VectorMA(in, backoff, normal, out);
710 for (i = 0;i < 3;i++)
711 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
720 The basic solid body movement clip that slides along multiple planes
721 Returns the clipflags if the velocity was modified (hit something solid)
725 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
728 // LordHavoc: increased from 5 to 32
729 #define MAX_CLIP_PLANES 32
730 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
732 int blocked, bumpcount;
733 int i, j, impact, numplanes;
735 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
740 VectorCopy(ent->fields.server->velocity, original_velocity);
741 VectorCopy(ent->fields.server->velocity, primal_velocity);
744 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
746 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
749 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
750 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
752 //if (trace.fraction < 0.002)
757 VectorCopy(ent->fields.server->origin, start);
758 start[2] += 3;//0.03125;
759 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
760 end[2] += 3;//0.03125;
761 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
762 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)))
764 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
770 for (i = 0;i < numplanes;i++)
772 VectorCopy(ent->fields.server->origin, start);
773 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
774 VectorMA(start, 3, planes[i], start);
775 VectorMA(end, 3, planes[i], end);
776 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
777 if (trace.fraction < testtrace.fraction)
780 VectorCopy(start, ent->fields.server->origin);
785 // VectorAdd(ent->fields.server->origin, planes[j], start);
791 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);
792 if (trace.fraction < 1)
793 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
798 if (trace.bmodelstartsolid)
800 // LordHavoc: note: this code is what makes entities stick in place
801 // if embedded in world only (you can walk through other objects if
803 // entity is trapped in another solid
804 VectorClear(ent->fields.server->velocity);
809 // break if it moved the entire distance
810 if (trace.fraction == 1)
812 VectorCopy(trace.endpos, ent->fields.server->origin);
818 Con_Printf ("SV_FlyMove: !trace.ent");
819 trace.ent = prog->edicts;
822 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
824 if (trace.plane.normal[2])
826 if (trace.plane.normal[2] > 0.7)
830 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
831 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
838 // save the trace for player extrafriction
840 VectorCopy(trace.plane.normal, stepnormal);
843 if (trace.fraction >= 0.001)
845 // actually covered some distance
846 VectorCopy(trace.endpos, ent->fields.server->origin);
847 VectorCopy(ent->fields.server->velocity, original_velocity);
851 // run the impact function
854 SV_Impact(ent, &trace);
856 // break if removed by the impact function
857 if (ent->priv.server->free)
861 time_left *= 1 - trace.fraction;
863 // clipped to another plane
864 if (numplanes >= MAX_CLIP_PLANES)
866 // this shouldn't really happen
867 VectorClear(ent->fields.server->velocity);
873 for (i = 0;i < numplanes;i++)
874 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
878 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
883 VectorCopy(trace.plane.normal, planes[numplanes]);
886 if (sv_newflymove.integer)
887 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
890 // modify original_velocity so it parallels all of the clip planes
891 for (i = 0;i < numplanes;i++)
893 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
894 for (j = 0;j < numplanes;j++)
899 if (DotProduct(new_velocity, planes[j]) < 0)
909 // go along this plane
910 VectorCopy(new_velocity, ent->fields.server->velocity);
914 // go along the crease
917 VectorClear(ent->fields.server->velocity);
921 CrossProduct(planes[0], planes[1], dir);
922 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
923 VectorNormalize(dir);
924 d = DotProduct(dir, ent->fields.server->velocity);
925 VectorScale(dir, d, ent->fields.server->velocity);
929 // if current velocity is against the original velocity,
930 // stop dead to avoid tiny occilations in sloping corners
931 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
933 VectorClear(ent->fields.server->velocity);
938 //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]);
941 if ((blocked & 1) == 0 && bumpcount > 1)
943 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
944 // flag ONGROUND if there's ground under it
945 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
949 // LordHavoc: this came from QW and allows you to get out of water more easily
950 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
951 VectorCopy(primal_velocity, ent->fields.server->velocity);
961 void SV_AddGravity (prvm_edict_t *ent)
966 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
967 if (val!=0 && val->_float)
968 ent_gravity = val->_float;
971 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
976 ===============================================================================
980 ===============================================================================
987 Does not change the entities velocity at all
990 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
996 VectorAdd (ent->fields.server->origin, push, end);
998 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1000 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1001 type = MOVE_NOMONSTERS; // only clip against bmodels
1005 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1006 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1009 VectorCopy (trace.endpos, ent->fields.server->origin);
1010 SV_LinkEdict (ent, true);
1012 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)))
1013 SV_Impact (ent, &trace);
1024 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1027 float savesolid, movetime2, pushltime;
1028 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1030 int numcheckentities;
1031 static prvm_edict_t *checkentities[MAX_EDICTS];
1032 model_t *pushermodel;
1034 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1036 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])
1038 pusher->fields.server->ltime += movetime;
1042 switch ((int) pusher->fields.server->solid)
1044 // LordHavoc: valid pusher types
1047 case SOLID_SLIDEBOX:
1048 case SOLID_CORPSE: // LordHavoc: this would be weird...
1050 // LordHavoc: no collisions
1053 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1054 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1055 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1056 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1057 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1058 pusher->fields.server->ltime += movetime;
1059 SV_LinkEdict (pusher, false);
1062 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1065 index = (int) pusher->fields.server->modelindex;
1066 if (index < 1 || index >= MAX_MODELS)
1068 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1071 pushermodel = sv.models[index];
1073 movetime2 = movetime;
1074 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1075 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1076 if (moveangle[0] || moveangle[2])
1078 for (i = 0;i < 3;i++)
1082 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1083 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1087 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1088 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1092 else if (moveangle[1])
1094 for (i = 0;i < 3;i++)
1098 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1099 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1103 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1104 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1110 for (i = 0;i < 3;i++)
1114 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1115 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1119 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1120 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1125 VectorNegate (moveangle, a);
1126 AngleVectorsFLU (a, forward, left, up);
1128 VectorCopy (pusher->fields.server->origin, pushorig);
1129 VectorCopy (pusher->fields.server->angles, pushang);
1130 pushltime = pusher->fields.server->ltime;
1132 // move the pusher to its final position
1134 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1135 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1136 pusher->fields.server->ltime += movetime;
1137 SV_LinkEdict (pusher, false);
1140 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1141 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1142 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);
1143 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1145 savesolid = pusher->fields.server->solid;
1147 // see if any solid entities are inside the final position
1150 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1151 for (e = 0;e < numcheckentities;e++)
1153 prvm_edict_t *check = checkentities[e];
1154 if (check->fields.server->movetype == MOVETYPE_NONE
1155 || check->fields.server->movetype == MOVETYPE_PUSH
1156 || check->fields.server->movetype == MOVETYPE_FOLLOW
1157 || check->fields.server->movetype == MOVETYPE_NOCLIP
1158 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1161 // if the entity is standing on the pusher, it will definitely be moved
1162 // if the entity is not standing on the pusher, but is in the pusher's
1163 // final position, move it
1164 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1166 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);
1167 if (!trace.startsolid)
1172 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1175 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1176 org2[0] = DotProduct (org, forward);
1177 org2[1] = DotProduct (org, left);
1178 org2[2] = DotProduct (org, up);
1179 VectorSubtract (org2, org, move);
1180 VectorAdd (move, move1, move);
1183 VectorCopy (move1, move);
1185 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1186 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1187 sv.moved_edicts[num_moved++] = check;
1189 // try moving the contacted entity
1190 pusher->fields.server->solid = SOLID_NOT;
1191 trace = SV_PushEntity (check, move, true);
1192 // FIXME: turn players specially
1193 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1194 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1195 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1197 // this trace.fraction < 1 check causes items to fall off of pushers
1198 // if they pass under or through a wall
1199 // the groundentity check causes items to fall off of ledges
1200 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1201 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1203 // if it is still inside the pusher, block
1204 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY);
1205 if (trace.startsolid)
1207 // try moving the contacted entity a tiny bit further to account for precision errors
1209 pusher->fields.server->solid = SOLID_NOT;
1210 VectorScale(move, 1.1, move2);
1211 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1212 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1213 SV_PushEntity (check, move2, true);
1214 pusher->fields.server->solid = savesolid;
1215 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);
1216 if (trace.startsolid)
1218 // try moving the contacted entity a tiny bit less to account for precision errors
1219 pusher->fields.server->solid = SOLID_NOT;
1220 VectorScale(move, 0.9, move2);
1221 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1222 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1223 SV_PushEntity (check, move2, true);
1224 pusher->fields.server->solid = savesolid;
1225 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);
1226 if (trace.startsolid)
1228 // still inside pusher, so it's really blocked
1231 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1233 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1236 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1237 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1241 VectorCopy (pushorig, pusher->fields.server->origin);
1242 VectorCopy (pushang, pusher->fields.server->angles);
1243 pusher->fields.server->ltime = pushltime;
1244 SV_LinkEdict (pusher, false);
1246 // move back any entities we already moved
1247 for (i = 0;i < num_moved;i++)
1249 prvm_edict_t *ed = sv.moved_edicts[i];
1250 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1251 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1252 SV_LinkEdict (ed, false);
1255 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1256 if (pusher->fields.server->blocked)
1258 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1259 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1260 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1267 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1268 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1269 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1278 void SV_Physics_Pusher (prvm_edict_t *ent)
1280 float thinktime, oldltime, movetime;
1282 oldltime = ent->fields.server->ltime;
1284 thinktime = ent->fields.server->nextthink;
1285 if (thinktime < ent->fields.server->ltime + sv.frametime)
1287 movetime = thinktime - ent->fields.server->ltime;
1292 movetime = sv.frametime;
1295 // advances ent->fields.server->ltime if not blocked
1296 SV_PushMove (ent, movetime);
1298 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1300 ent->fields.server->nextthink = 0;
1301 prog->globals.server->time = sv.time;
1302 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1303 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1304 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1310 ===============================================================================
1314 ===============================================================================
1317 static float unstickoffsets[] =
1351 This is a big hack to try and fix the rare case of getting stuck in the world
1355 void SV_CheckStuck (prvm_edict_t *ent)
1360 if (!SV_TestEntityPosition(ent, vec3_origin))
1362 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1366 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1368 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1370 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]);
1371 SV_LinkEdict (ent, true);
1376 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1377 if (!SV_TestEntityPosition(ent, offset))
1379 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1380 SV_LinkEdict (ent, true);
1384 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1387 static void SV_UnstickEntity (prvm_edict_t *ent)
1391 // if not stuck in a bmodel, just return
1392 if (!SV_TestEntityPosition(ent, vec3_origin))
1395 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1397 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1399 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]);
1400 SV_LinkEdict (ent, true);
1405 if (developer.integer >= 100)
1406 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1415 qboolean SV_CheckWater (prvm_edict_t *ent)
1418 int nNativeContents;
1421 point[0] = ent->fields.server->origin[0];
1422 point[1] = ent->fields.server->origin[1];
1423 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1425 // DRESK - Support for Entity Contents Transition Event
1426 // NOTE: Some logic needed to be slightly re-ordered
1427 // to not affect performance and allow for the feature.
1429 // Acquire Super Contents Prior to Resets
1430 cont = SV_PointSuperContents(point);
1431 // Acquire Native Contents Here
1432 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1434 // DRESK - Support for Entity Contents Transition Event
1435 if(ent->fields.server->watertype)
1436 // Entity did NOT Spawn; Check
1437 SV_CheckContentsTransition(ent, nNativeContents);
1440 ent->fields.server->waterlevel = 0;
1441 ent->fields.server->watertype = CONTENTS_EMPTY;
1442 cont = SV_PointSuperContents(point);
1443 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1445 ent->fields.server->watertype = nNativeContents;
1446 ent->fields.server->waterlevel = 1;
1447 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1448 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1450 ent->fields.server->waterlevel = 2;
1451 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1452 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1453 ent->fields.server->waterlevel = 3;
1457 return ent->fields.server->waterlevel > 1;
1466 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1469 vec3_t forward, into, side;
1471 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1472 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1474 // cut the tangential velocity
1475 i = DotProduct (stepnormal, ent->fields.server->velocity);
1476 VectorScale (stepnormal, i, into);
1477 VectorSubtract (ent->fields.server->velocity, into, side);
1478 ent->fields.server->velocity[0] = side[0] * (1 + d);
1479 ent->fields.server->velocity[1] = side[1] * (1 + d);
1485 =====================
1488 Player has come to a dead stop, possibly due to the problem with limited
1489 float precision at some angle joins in the BSP hull.
1491 Try fixing by pushing one pixel in each direction.
1493 This is a hack, but in the interest of good gameplay...
1494 ======================
1496 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1501 VectorCopy (ent->fields.server->origin, oldorg);
1504 for (i=0 ; i<8 ; i++)
1506 // try pushing a little in an axial direction
1509 case 0: dir[0] = 2; dir[1] = 0; break;
1510 case 1: dir[0] = 0; dir[1] = 2; break;
1511 case 2: dir[0] = -2; dir[1] = 0; break;
1512 case 3: dir[0] = 0; dir[1] = -2; break;
1513 case 4: dir[0] = 2; dir[1] = 2; break;
1514 case 5: dir[0] = -2; dir[1] = 2; break;
1515 case 6: dir[0] = 2; dir[1] = -2; break;
1516 case 7: dir[0] = -2; dir[1] = -2; break;
1519 SV_PushEntity (ent, dir, false);
1521 // retry the original move
1522 ent->fields.server->velocity[0] = oldvel[0];
1523 ent->fields.server->velocity[1] = oldvel[1];
1524 ent->fields.server->velocity[2] = 0;
1525 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1527 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1528 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1530 Con_DPrint("TryUnstick - success.\n");
1534 // go back to the original pos and try again
1535 VectorCopy (oldorg, ent->fields.server->origin);
1539 VectorClear (ent->fields.server->velocity);
1540 Con_DPrint("TryUnstick - failure.\n");
1546 =====================
1549 Only used by players
1550 ======================
1552 void SV_WalkMove (prvm_edict_t *ent)
1554 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1555 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1558 // if frametime is 0 (due to client sending the same timestamp twice),
1560 if (sv.frametime <= 0)
1563 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1565 SV_CheckVelocity(ent);
1567 // do a regular slide move unless it looks like you ran into a step
1568 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1570 VectorCopy (ent->fields.server->origin, start_origin);
1571 VectorCopy (ent->fields.server->velocity, start_velocity);
1573 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1575 // if the move did not hit the ground at any point, we're not on ground
1577 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1579 SV_CheckVelocity(ent);
1581 VectorCopy(ent->fields.server->origin, originalmove_origin);
1582 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1583 originalmove_clip = clip;
1584 originalmove_flags = (int)ent->fields.server->flags;
1585 originalmove_groundentity = ent->fields.server->groundentity;
1587 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1590 if (sv_nostep.integer)
1593 // if move didn't block on a step, return
1596 // if move was not trying to move into the step, return
1597 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1600 if (ent->fields.server->movetype != MOVETYPE_FLY)
1602 // return if gibbed by a trigger
1603 if (ent->fields.server->movetype != MOVETYPE_WALK)
1606 // only step up while jumping if that is enabled
1607 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1608 if (!oldonground && ent->fields.server->waterlevel == 0)
1612 // try moving up and forward to go up a step
1613 // back to start pos
1614 VectorCopy (start_origin, ent->fields.server->origin);
1615 VectorCopy (start_velocity, ent->fields.server->velocity);
1618 VectorClear (upmove);
1619 upmove[2] = sv_stepheight.value;
1620 // FIXME: don't link?
1621 SV_PushEntity(ent, upmove, false);
1624 ent->fields.server->velocity[2] = 0;
1625 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1626 ent->fields.server->velocity[2] += start_velocity[2];
1628 SV_CheckVelocity(ent);
1630 // check for stuckness, possibly due to the limited precision of floats
1631 // in the clipping hulls
1633 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1634 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1636 //Con_Printf("wall\n");
1637 // stepping up didn't make any progress, revert to original move
1638 VectorCopy(originalmove_origin, ent->fields.server->origin);
1639 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1640 //clip = originalmove_clip;
1641 ent->fields.server->flags = originalmove_flags;
1642 ent->fields.server->groundentity = originalmove_groundentity;
1643 // now try to unstick if needed
1644 //clip = SV_TryUnstick (ent, oldvel);
1648 //Con_Printf("step - ");
1650 // extra friction based on view angle
1651 if (clip & 2 && sv_wallfriction.integer)
1652 SV_WallFriction (ent, stepnormal);
1654 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1655 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))
1659 VectorClear (downmove);
1660 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1661 // FIXME: don't link?
1662 downtrace = SV_PushEntity (ent, downmove, false);
1664 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1666 // this has been disabled so that you can't jump when you are stepping
1667 // up while already jumping (also known as the Quake2 double jump bug)
1669 // LordHavoc: disabled this check so you can walk on monsters/players
1670 //if (ent->fields.server->solid == SOLID_BSP)
1672 //Con_Printf("onground\n");
1673 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1674 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1680 //Con_Printf("slope\n");
1681 // if the push down didn't end up on good ground, use the move without
1682 // the step up. This happens near wall / slope combinations, and can
1683 // cause the player to hop up higher on a slope too steep to climb
1684 VectorCopy(originalmove_origin, ent->fields.server->origin);
1685 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1686 //clip = originalmove_clip;
1687 ent->fields.server->flags = originalmove_flags;
1688 ent->fields.server->groundentity = originalmove_groundentity;
1691 SV_CheckVelocity(ent);
1694 //============================================================================
1700 Entities that are "stuck" to another entity
1703 void SV_Physics_Follow (prvm_edict_t *ent)
1705 vec3_t vf, vr, vu, angles, v;
1709 if (!SV_RunThink (ent))
1712 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1713 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1714 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])
1716 // quick case for no rotation
1717 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1721 angles[0] = -ent->fields.server->punchangle[0];
1722 angles[1] = ent->fields.server->punchangle[1];
1723 angles[2] = ent->fields.server->punchangle[2];
1724 AngleVectors (angles, vf, vr, vu);
1725 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];
1726 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];
1727 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];
1728 angles[0] = -e->fields.server->angles[0];
1729 angles[1] = e->fields.server->angles[1];
1730 angles[2] = e->fields.server->angles[2];
1731 AngleVectors (angles, vf, vr, vu);
1732 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1733 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1734 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1736 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1737 SV_LinkEdict (ent, true);
1741 ==============================================================================
1745 ==============================================================================
1750 SV_CheckWaterTransition
1754 void SV_CheckWaterTransition (prvm_edict_t *ent)
1757 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1758 if (!ent->fields.server->watertype)
1760 // just spawned here
1761 ent->fields.server->watertype = cont;
1762 ent->fields.server->waterlevel = 1;
1766 // DRESK - Support for Entity Contents Transition Event
1767 // NOTE: Call here BEFORE updating the watertype below,
1768 // and suppress watersplash sound if a valid function
1769 // call was made to allow for custom "splash" sounds.
1770 if( !SV_CheckContentsTransition(ent, cont) )
1771 { // Contents Transition Function Invalid; Potentially Play Water Sound
1772 // check if the entity crossed into or out of water
1773 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1774 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1777 if (cont <= CONTENTS_WATER)
1779 ent->fields.server->watertype = cont;
1780 ent->fields.server->waterlevel = 1;
1784 ent->fields.server->watertype = CONTENTS_EMPTY;
1785 ent->fields.server->waterlevel = 0;
1793 Toss, bounce, and fly movement. When onground, do nothing.
1796 void SV_Physics_Toss (prvm_edict_t *ent)
1801 // if onground, return without moving
1802 if ((int)ent->fields.server->flags & FL_ONGROUND)
1804 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1806 // don't stick to ground if onground and moving upward
1807 ent->fields.server->flags -= FL_ONGROUND;
1809 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1811 // we can trust FL_ONGROUND if groundentity is world because it never moves
1814 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1816 // if ent was supported by a brush model on previous frame,
1817 // and groundentity is now freed, set groundentity to 0 (world)
1818 // which leaves it suspended in the air
1819 ent->fields.server->groundentity = 0;
1823 ent->priv.server->suspendedinairflag = false;
1825 SV_CheckVelocity (ent);
1828 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1829 SV_AddGravity (ent);
1832 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1835 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1836 trace = SV_PushEntity (ent, move, true);
1837 if (ent->priv.server->free)
1839 if (trace.bmodelstartsolid)
1841 // try to unstick the entity
1842 SV_UnstickEntity(ent);
1843 trace = SV_PushEntity (ent, move, false);
1844 if (ent->priv.server->free)
1848 if (trace.fraction < 1)
1850 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1852 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1853 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1855 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1858 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1859 // LordHavoc: fixed grenades not bouncing when fired down a slope
1860 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1862 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1863 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1865 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1866 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1867 VectorClear (ent->fields.server->velocity);
1868 VectorClear (ent->fields.server->avelocity);
1871 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1875 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1877 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1878 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1879 VectorClear (ent->fields.server->velocity);
1880 VectorClear (ent->fields.server->avelocity);
1883 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1888 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1889 if (trace.plane.normal[2] > 0.7)
1891 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1892 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1893 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1894 ent->priv.server->suspendedinairflag = true;
1895 VectorClear (ent->fields.server->velocity);
1896 VectorClear (ent->fields.server->avelocity);
1899 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1903 // check for in water
1904 SV_CheckWaterTransition (ent);
1908 ===============================================================================
1912 ===============================================================================
1919 Monsters freefall when they don't have a ground entity, otherwise
1920 all movement is done with discrete steps.
1922 This is also used for objects that have become still on the ground, but
1923 will fall if the floor is pulled out from under them.
1926 void SV_Physics_Step (prvm_edict_t *ent)
1928 int flags = (int)ent->fields.server->flags;
1929 // don't fall at all if fly/swim
1930 if (!(flags & (FL_FLY | FL_SWIM)))
1932 if (flags & FL_ONGROUND)
1934 // freefall if onground and moving upward
1935 // freefall if not standing on a world surface (it may be a lift or trap door)
1936 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1938 ent->fields.server->flags -= FL_ONGROUND;
1940 SV_CheckVelocity(ent);
1941 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1942 SV_LinkEdict(ent, true);
1947 // freefall if not onground
1948 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1951 SV_CheckVelocity(ent);
1952 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1953 SV_LinkEdict(ent, true);
1956 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1957 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1964 SV_CheckWaterTransition(ent);
1967 //============================================================================
1969 static void SV_Physics_Entity (prvm_edict_t *ent)
1971 // don't run a move on newly spawned projectiles as it messes up movement
1972 // interpolation and rocket trails
1973 qboolean runmove = ent->priv.server->move;
1974 ent->priv.server->move = true;
1975 switch ((int) ent->fields.server->movetype)
1978 case MOVETYPE_FAKEPUSH:
1979 SV_Physics_Pusher (ent);
1982 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1983 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1986 case MOVETYPE_FOLLOW:
1987 SV_Physics_Follow (ent);
1989 case MOVETYPE_NOCLIP:
1990 if (SV_RunThink(ent))
1993 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1994 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1996 SV_LinkEdict(ent, false);
1999 SV_Physics_Step (ent);
2002 if (SV_RunThink (ent))
2004 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2005 SV_AddGravity (ent);
2006 SV_CheckStuck (ent);
2008 SV_LinkEdict (ent, true);
2012 case MOVETYPE_BOUNCE:
2013 case MOVETYPE_BOUNCEMISSILE:
2014 case MOVETYPE_FLYMISSILE:
2017 if (SV_RunThink (ent) && runmove)
2018 SV_Physics_Toss (ent);
2021 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2026 void SV_Physics_ClientMove(void)
2029 ent = host_client->edict;
2031 // call player physics, this needs the proper frametime
2032 prog->globals.server->frametime = sv.frametime;
2035 // call standard client pre-think, with frametime = 0
2036 prog->globals.server->time = sv.time;
2037 prog->globals.server->frametime = 0;
2038 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2039 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2040 prog->globals.server->frametime = sv.frametime;
2042 // make sure the velocity is sane (not a NaN)
2043 SV_CheckVelocity(ent);
2044 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2045 // player_run/player_stand1 does not horribly malfunction if the
2046 // velocity becomes a number that is both == 0 and != 0
2047 // (sounds to me like NaN but to be absolutely safe...)
2048 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2049 VectorClear(ent->fields.server->velocity);
2051 // perform MOVETYPE_WALK behavior
2052 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2053 SV_AddGravity (ent);
2054 SV_CheckStuck (ent);
2057 SV_CheckVelocity (ent);
2059 SV_LinkEdict (ent, true);
2061 SV_CheckVelocity (ent);
2063 // call standard player post-think, with frametime = 0
2064 prog->globals.server->time = sv.time;
2065 prog->globals.server->frametime = 0;
2066 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2067 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2068 prog->globals.server->frametime = sv.frametime;
2070 if(ent->fields.server->fixangle)
2072 // angle fixing was requested by physics code...
2073 // so store the current angles for later use
2074 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2075 host_client->fixangle_angles_set = TRUE;
2077 // and clear fixangle for the next frame
2078 ent->fields.server->fixangle = 0;
2082 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2084 // don't do physics on disconnected clients, FrikBot relies on this
2085 if (!host_client->spawned)
2087 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2091 // don't run physics here if running asynchronously
2092 if (host_client->clmovement_skipphysicsframes <= 0)
2095 // make sure the velocity is sane (not a NaN)
2096 SV_CheckVelocity(ent);
2097 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2098 // player_run/player_stand1 does not horribly malfunction if the
2099 // velocity becomes a number that is both == 0 and != 0
2100 // (sounds to me like NaN but to be absolutely safe...)
2101 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2102 VectorClear(ent->fields.server->velocity);
2104 // call standard client pre-think
2105 prog->globals.server->time = sv.time;
2106 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2107 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2108 SV_CheckVelocity (ent);
2110 switch ((int) ent->fields.server->movetype)
2113 case MOVETYPE_FAKEPUSH:
2114 SV_Physics_Pusher (ent);
2117 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2118 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2121 case MOVETYPE_FOLLOW:
2122 SV_Physics_Follow (ent);
2124 case MOVETYPE_NOCLIP:
2127 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2128 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2131 SV_Physics_Step (ent);
2135 // don't run physics here if running asynchronously
2136 if (host_client->clmovement_skipphysicsframes <= 0)
2138 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2139 SV_AddGravity (ent);
2140 SV_CheckStuck (ent);
2145 case MOVETYPE_BOUNCE:
2146 case MOVETYPE_BOUNCEMISSILE:
2147 case MOVETYPE_FLYMISSILE:
2150 SV_Physics_Toss (ent);
2154 SV_CheckWater (ent);
2158 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2162 // decrement the countdown variable used to decide when to go back to
2163 // synchronous physics
2164 if (host_client->clmovement_skipphysicsframes > 0)
2165 host_client->clmovement_skipphysicsframes--;
2167 SV_CheckVelocity (ent);
2169 SV_LinkEdict (ent, true);
2171 SV_CheckVelocity (ent);
2173 // call standard player post-think
2174 prog->globals.server->time = sv.time;
2175 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2176 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2178 if(ent->fields.server->fixangle)
2180 // angle fixing was requested by physics code...
2181 // so store the current angles for later use
2182 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2183 host_client->fixangle_angles_set = TRUE;
2185 // and clear fixangle for the next frame
2186 ent->fields.server->fixangle = 0;
2196 void SV_Physics (void)
2201 // let the progs know that a new frame has started
2202 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2203 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2204 prog->globals.server->time = sv.time;
2205 prog->globals.server->frametime = sv.frametime;
2206 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2209 // treat each object in turn
2212 // if force_retouch, relink all the entities
2213 if (prog->globals.server->force_retouch > 0)
2214 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2215 if (!ent->priv.server->free)
2216 SV_LinkEdict (ent, true); // force retouch even for stationary
2218 // run physics on the client entities
2219 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2220 if (!ent->priv.server->free)
2221 SV_Physics_ClientEntity(ent);
2223 // run physics on all the non-client entities
2224 if (!sv_freezenonclients.integer)
2225 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2226 if (!ent->priv.server->free)
2227 SV_Physics_Entity(ent);
2229 if (prog->globals.server->force_retouch > 0)
2230 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2232 // LordHavoc: endframe support
2233 if (prog->funcoffsets.EndFrame)
2235 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2236 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2237 prog->globals.server->time = sv.time;
2238 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2241 // decrement prog->num_edicts if the highest number entities died
2242 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2244 if (!sv_freezenonclients.integer)
2245 sv.time += sv.frametime;