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 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
47 ===============================================================================
51 ===============================================================================
54 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
59 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
60 if (val && val->_float)
61 return (int)val->_float;
62 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
64 if ((int)passedict->fields.server->flags & FL_MONSTER)
65 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
67 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
69 else if (passedict->fields.server->solid == SOLID_CORPSE)
70 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
71 else if (passedict->fields.server->solid == SOLID_TRIGGER)
72 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
74 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
77 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
85 #if COLLISIONPARANOID >= 1
86 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)
88 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)
91 vec3_t hullmins, hullmaxs;
92 int i, bodysupercontents;
95 prvm_edict_t *traceowner, *touch;
97 // bounding box of entire move area
98 vec3_t clipboxmins, clipboxmaxs;
99 // size of the moving object
100 vec3_t clipmins, clipmaxs;
101 // size when clipping against monsters
102 vec3_t clipmins2, clipmaxs2;
103 // start and end origin of move
104 vec3_t clipstart, clipend;
107 // matrices to transform into/out of other entity's space
108 matrix4x4_t matrix, imatrix;
109 // model of other entity
111 // list of entities to test for collisions
113 prvm_edict_t *touchedicts[MAX_EDICTS];
115 VectorCopy(start, clipstart);
116 VectorCopy(end, clipend);
117 VectorCopy(mins, clipmins);
118 VectorCopy(maxs, clipmaxs);
119 VectorCopy(mins, clipmins2);
120 VectorCopy(maxs, clipmaxs2);
121 #if COLLISIONPARANOID >= 3
122 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
126 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
127 cliptrace.bmodelstartsolid = cliptrace.startsolid;
128 if (cliptrace.startsolid || cliptrace.fraction < 1)
129 cliptrace.ent = prog->edicts;
130 if (type == MOVE_WORLDONLY)
133 if (type == MOVE_MISSILE)
135 // LordHavoc: modified this, was = -15, now -= 15
136 for (i = 0;i < 3;i++)
143 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
144 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
145 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
148 VectorCopy(clipmins, hullmins);
149 VectorCopy(clipmaxs, hullmaxs);
152 // create the bounding box of the entire move
153 for (i = 0;i < 3;i++)
155 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
156 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
159 // debug override to test against everything
160 if (sv_debugmove.integer)
162 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
163 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
166 // if the passedict is world, make it NULL (to avoid two checks each time)
167 if (passedict == prog->edicts)
169 // precalculate prog value for passedict for comparisons
170 passedictprog = PRVM_EDICT_TO_PROG(passedict);
171 // figure out whether this is a point trace for comparisons
172 pointtrace = VectorCompare(clipmins, clipmaxs);
173 // precalculate passedict's owner edict pointer for comparisons
174 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
177 // because this uses World_EntitiestoBox, we know all entity boxes overlap
178 // the clip region, so we can skip culling checks in the loop below
179 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
180 if (numtouchedicts > MAX_EDICTS)
182 // this never happens
183 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
184 numtouchedicts = MAX_EDICTS;
186 for (i = 0;i < numtouchedicts;i++)
188 touch = touchedicts[i];
190 if (touch->fields.server->solid < SOLID_BBOX)
192 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
197 // don't clip against self
198 if (passedict == touch)
200 // don't clip owned entities against owner
201 if (traceowner == touch)
203 // don't clip owner against owned entities
204 if (passedictprog == touch->fields.server->owner)
206 // don't clip points against points (they can't collide)
207 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
211 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
213 // might interact, so do an exact clip
215 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
217 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
218 // if the modelindex is 0, it shouldn't be SOLID_BSP!
219 if (modelindex > 0 && modelindex < MAX_MODELS)
220 model = sv.models[(int)touch->fields.server->modelindex];
223 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);
225 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
226 Matrix4x4_Invert_Simple(&imatrix, &matrix);
227 if ((int)touch->fields.server->flags & FL_MONSTER)
228 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins2, clipmaxs2, clipend, hitsupercontentsmask);
230 Collision_ClipToGenericEntity(&trace, model, touch->fields.server->frame, touch->fields.server->mins, touch->fields.server->maxs, bodysupercontents, &matrix, &imatrix, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
232 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
238 #if COLLISIONPARANOID >= 1
239 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)
244 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
247 VectorCopy(trace.endpos, temp);
248 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
249 #if COLLISIONPARANOID < 3
250 if (trace.startsolid || endstuck)
252 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" : "");
258 int SV_PointSuperContents(const vec3_t point)
260 int supercontents = 0;
264 // matrices to transform into/out of other entity's space
265 matrix4x4_t matrix, imatrix;
266 // model of other entity
268 unsigned int modelindex;
270 // list of entities to test for collisions
272 prvm_edict_t *touchedicts[MAX_EDICTS];
274 // get world supercontents at this point
275 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
276 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
278 // if sv_gameplayfix_swiminbmodels is off we're done
279 if (!sv_gameplayfix_swiminbmodels.integer)
280 return supercontents;
282 // get list of entities at this point
283 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
284 if (numtouchedicts > MAX_EDICTS)
286 // this never happens
287 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
288 numtouchedicts = MAX_EDICTS;
290 for (i = 0;i < numtouchedicts;i++)
292 touch = touchedicts[i];
294 // we only care about SOLID_BSP for pointcontents
295 if (touch->fields.server->solid != SOLID_BSP)
298 // might interact, so do an exact clip
299 modelindex = (unsigned int)touch->fields.server->modelindex;
300 if (modelindex >= MAX_MODELS)
302 model = sv.models[(int)touch->fields.server->modelindex];
303 if (!model || !model->PointSuperContents)
305 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);
306 Matrix4x4_Invert_Simple(&imatrix, &matrix);
307 Matrix4x4_Transform(&imatrix, point, transformed);
308 frame = (int)touch->fields.server->frame;
309 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
312 return supercontents;
316 ===============================================================================
318 Linking entities into the world culling system
320 ===============================================================================
323 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
325 int i, numtouchedicts, old_self, old_other;
326 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
328 // build a list of edicts to touch, because the link loop can be corrupted
329 // by IncreaseEdicts called during touch functions
330 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
331 if (numtouchedicts > MAX_EDICTS)
333 // this never happens
334 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
335 numtouchedicts = MAX_EDICTS;
338 old_self = prog->globals.server->self;
339 old_other = prog->globals.server->other;
340 for (i = 0;i < numtouchedicts;i++)
342 touch = touchedicts[i];
343 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
346 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
347 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
348 prog->globals.server->time = sv.time;
349 prog->globals.server->trace_allsolid = false;
350 prog->globals.server->trace_startsolid = false;
351 prog->globals.server->trace_fraction = 1;
352 prog->globals.server->trace_inwater = false;
353 prog->globals.server->trace_inopen = true;
354 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
355 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
356 prog->globals.server->trace_plane_dist = 0;
357 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
358 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
360 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
362 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
364 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
366 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
369 prog->globals.server->self = old_self;
370 prog->globals.server->other = old_other;
379 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
384 if (ent == prog->edicts)
385 return; // don't add the world
387 if (ent->priv.server->free)
392 if (ent->fields.server->solid == SOLID_BSP)
394 int modelindex = (int)ent->fields.server->modelindex;
395 if (modelindex < 0 || modelindex > MAX_MODELS)
397 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
400 model = sv.models[modelindex];
403 if (!model->TraceBox && developer.integer >= 1)
404 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
406 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
408 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
409 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
411 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
413 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
414 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
418 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
419 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
424 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
425 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
426 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
431 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
432 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
436 // to make items easier to pick up and allow them to be grabbed off
437 // of shelves, the abs sizes are expanded
439 if ((int)ent->fields.server->flags & FL_ITEM)
450 // because movement is clipped an epsilon away from an actual edge,
451 // we must fully check even when bounding boxes don't quite touch
460 VectorCopy(mins, ent->fields.server->absmin);
461 VectorCopy(maxs, ent->fields.server->absmax);
463 World_LinkEdict(&sv.world, ent, mins, maxs);
465 // if touch_triggers, call touch on all entities overlapping this box
466 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
467 SV_LinkEdict_TouchAreaGrid(ent);
471 ===============================================================================
475 ===============================================================================
480 SV_TestEntityPosition
482 returns true if the entity is in solid currently
485 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
489 VectorAdd(ent->fields.server->origin, offset, org);
490 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
491 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
495 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
497 // q1bsp/hlbsp use hulls and if the entity does not exactly match
498 // a hull size it is incorrectly tested, so this code tries to
499 // 'fix' it slightly...
500 // FIXME: this breaks entities larger than the hull size
503 VectorAdd(org, ent->fields.server->mins, m1);
504 VectorAdd(org, ent->fields.server->maxs, m2);
505 VectorSubtract(m2, m1, s);
506 #define EPSILON (1.0f / 32.0f)
507 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
508 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
509 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
510 for (i = 0;i < 8;i++)
512 v[0] = (i & 1) ? m2[0] : m1[0];
513 v[1] = (i & 2) ? m2[1] : m1[1];
514 v[2] = (i & 4) ? m2[2] : m1[2];
515 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
520 // if the trace found a better position for the entity, move it there
521 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
522 VectorCopy(trace.endpos, ent->fields.server->origin);
531 void SV_CheckAllEnts (void)
536 // see if any solid entities are inside the final position
537 check = PRVM_NEXT_EDICT(prog->edicts);
538 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
540 if (check->priv.server->free)
542 if (check->fields.server->movetype == MOVETYPE_PUSH
543 || check->fields.server->movetype == MOVETYPE_NONE
544 || check->fields.server->movetype == MOVETYPE_FOLLOW
545 || check->fields.server->movetype == MOVETYPE_NOCLIP)
548 if (SV_TestEntityPosition (check, vec3_origin))
549 Con_Print("entity in invalid position\n");
553 // DRESK - Support for Entity Contents Transition Event
556 SV_CheckContentsTransition
558 returns true if entity had a valid contentstransition function call
561 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
563 int bValidFunctionCall;
564 prvm_eval_t *contentstransition;
566 // Default Valid Function Call to False
567 bValidFunctionCall = false;
569 if(ent->fields.server->watertype != nContents)
570 { // Changed Contents
571 // Acquire Contents Transition Function from QC
572 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
574 if(contentstransition->function)
575 { // Valid Function; Execute
576 // Assign Valid Function
577 bValidFunctionCall = true;
578 // Prepare Parameters (Original Contents, New Contents)
580 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
582 PRVM_G_FLOAT(OFS_PARM1) = nContents;
584 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
585 // Execute VM Function
586 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
590 // Return if Function Call was Valid
591 return bValidFunctionCall;
600 void SV_CheckVelocity (prvm_edict_t *ent)
608 for (i=0 ; i<3 ; i++)
610 if (IS_NAN(ent->fields.server->velocity[i]))
612 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
613 ent->fields.server->velocity[i] = 0;
615 if (IS_NAN(ent->fields.server->origin[i]))
617 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
618 ent->fields.server->origin[i] = 0;
622 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
623 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
624 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
626 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
627 ent->fields.server->velocity[0] *= wishspeed;
628 ent->fields.server->velocity[1] *= wishspeed;
629 ent->fields.server->velocity[2] *= wishspeed;
637 Runs thinking code if time. There is some play in the exact time the think
638 function will be called, because it is called before any movement is done
639 in a frame. Not used for pushmove objects, because they must be exact.
640 Returns false if the entity removed itself.
643 qboolean SV_RunThink (prvm_edict_t *ent)
647 // don't let things stay in the past.
648 // it is possible to start that way by a trigger with a local time.
649 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
652 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
654 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
655 ent->fields.server->nextthink = 0;
656 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
657 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
658 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
659 // mods often set nextthink to time to cause a think every frame,
660 // we don't want to loop in that case, so exit if the new nextthink is
661 // <= the time the qc was told, also exit if it is past the end of the
663 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
666 return !ent->priv.server->free;
673 Two entities have touched, so run their touch functions
676 extern void VM_SetTraceGlobals(const trace_t *trace);
677 extern sizebuf_t vm_tempstringsbuf;
678 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
680 int restorevm_tempstringsbuf_cursize;
681 int old_self, old_other;
682 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
685 old_self = prog->globals.server->self;
686 old_other = prog->globals.server->other;
687 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
689 VM_SetTraceGlobals(trace);
691 prog->globals.server->time = sv.time;
692 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
694 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
695 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
696 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
699 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
701 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
702 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
703 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
704 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
705 prog->globals.server->trace_plane_dist = -trace->plane.dist;
706 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
707 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
709 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
711 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
713 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
715 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
718 prog->globals.server->self = old_self;
719 prog->globals.server->other = old_other;
720 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
728 Slide off of the impacting object
729 returns the blocked flags (1 = floor, 2 = step / wall)
732 #define STOP_EPSILON 0.1
733 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
738 backoff = -DotProduct (in, normal) * overbounce;
739 VectorMA(in, backoff, normal, out);
741 for (i = 0;i < 3;i++)
742 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
751 The basic solid body movement clip that slides along multiple planes
752 Returns the clipflags if the velocity was modified (hit something solid)
756 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
759 // LordHavoc: increased from 5 to 32
760 #define MAX_CLIP_PLANES 32
761 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal, int hitsupercontentsmask)
763 int blocked, bumpcount;
764 int i, j, impact, numplanes;
766 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
771 VectorCopy(ent->fields.server->velocity, original_velocity);
772 VectorCopy(ent->fields.server->velocity, primal_velocity);
775 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
777 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
780 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
781 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
783 //if (trace.fraction < 0.002)
788 VectorCopy(ent->fields.server->origin, start);
789 start[2] += 3;//0.03125;
790 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
791 end[2] += 3;//0.03125;
792 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
793 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)))
795 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
801 for (i = 0;i < numplanes;i++)
803 VectorCopy(ent->fields.server->origin, start);
804 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
805 VectorMA(start, 3, planes[i], start);
806 VectorMA(end, 3, planes[i], end);
807 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
808 if (trace.fraction < testtrace.fraction)
811 VectorCopy(start, ent->fields.server->origin);
816 // VectorAdd(ent->fields.server->origin, planes[j], start);
822 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);
823 if (trace.fraction < 1)
824 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
829 if (trace.bmodelstartsolid)
831 // LordHavoc: note: this code is what makes entities stick in place
832 // if embedded in world only (you can walk through other objects if
834 // entity is trapped in another solid
835 VectorClear(ent->fields.server->velocity);
840 // break if it moved the entire distance
841 if (trace.fraction == 1)
843 VectorCopy(trace.endpos, ent->fields.server->origin);
849 Con_Printf ("SV_FlyMove: !trace.ent");
850 trace.ent = prog->edicts;
853 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
855 if (trace.plane.normal[2])
857 if (trace.plane.normal[2] > 0.7)
861 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
862 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
869 // save the trace for player extrafriction
871 VectorCopy(trace.plane.normal, stepnormal);
874 if (trace.fraction >= 0.001)
876 // actually covered some distance
877 VectorCopy(trace.endpos, ent->fields.server->origin);
878 VectorCopy(ent->fields.server->velocity, original_velocity);
882 // run the impact function
885 SV_Impact(ent, &trace);
887 // break if removed by the impact function
888 if (ent->priv.server->free)
892 time_left *= 1 - trace.fraction;
894 // clipped to another plane
895 if (numplanes >= MAX_CLIP_PLANES)
897 // this shouldn't really happen
898 VectorClear(ent->fields.server->velocity);
904 for (i = 0;i < numplanes;i++)
905 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
909 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
914 VectorCopy(trace.plane.normal, planes[numplanes]);
917 if (sv_newflymove.integer)
918 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
921 // modify original_velocity so it parallels all of the clip planes
922 for (i = 0;i < numplanes;i++)
924 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
925 for (j = 0;j < numplanes;j++)
930 if (DotProduct(new_velocity, planes[j]) < 0)
940 // go along this plane
941 VectorCopy(new_velocity, ent->fields.server->velocity);
945 // go along the crease
948 VectorClear(ent->fields.server->velocity);
952 CrossProduct(planes[0], planes[1], dir);
953 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
954 VectorNormalize(dir);
955 d = DotProduct(dir, ent->fields.server->velocity);
956 VectorScale(dir, d, ent->fields.server->velocity);
960 // if current velocity is against the original velocity,
961 // stop dead to avoid tiny occilations in sloping corners
962 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
964 VectorClear(ent->fields.server->velocity);
969 //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]);
972 if ((blocked & 1) == 0 && bumpcount > 1)
974 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
975 // flag ONGROUND if there's ground under it
976 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
980 // LordHavoc: this came from QW and allows you to get out of water more easily
981 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
982 VectorCopy(primal_velocity, ent->fields.server->velocity);
992 void SV_AddGravity (prvm_edict_t *ent)
997 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
998 if (val!=0 && val->_float)
999 ent_gravity = val->_float;
1002 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
1007 ===============================================================================
1011 ===============================================================================
1018 Does not change the entities velocity at all
1021 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
1027 VectorAdd (ent->fields.server->origin, push, end);
1029 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1030 type = MOVE_MISSILE;
1031 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1032 type = MOVE_NOMONSTERS; // only clip against bmodels
1036 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1037 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1040 VectorCopy (trace.endpos, ent->fields.server->origin);
1041 SV_LinkEdict (ent, true);
1043 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)))
1044 SV_Impact (ent, &trace);
1055 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1058 float savesolid, movetime2, pushltime;
1059 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1061 int numcheckentities;
1062 static prvm_edict_t *checkentities[MAX_EDICTS];
1063 model_t *pushermodel;
1065 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1066 unsigned short moved_edicts[MAX_EDICTS];
1068 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])
1070 pusher->fields.server->ltime += movetime;
1074 switch ((int) pusher->fields.server->solid)
1076 // LordHavoc: valid pusher types
1079 case SOLID_SLIDEBOX:
1080 case SOLID_CORPSE: // LordHavoc: this would be weird...
1082 // LordHavoc: no collisions
1085 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1086 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1087 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1088 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1089 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1090 pusher->fields.server->ltime += movetime;
1091 SV_LinkEdict (pusher, false);
1094 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1097 index = (int) pusher->fields.server->modelindex;
1098 if (index < 1 || index >= MAX_MODELS)
1100 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1103 pushermodel = sv.models[index];
1105 movetime2 = movetime;
1106 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1107 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1108 if (moveangle[0] || moveangle[2])
1110 for (i = 0;i < 3;i++)
1114 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1115 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1119 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1120 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1124 else if (moveangle[1])
1126 for (i = 0;i < 3;i++)
1130 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1131 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1135 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1136 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1142 for (i = 0;i < 3;i++)
1146 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1147 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1151 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1152 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1157 VectorNegate (moveangle, a);
1158 AngleVectorsFLU (a, forward, left, up);
1160 VectorCopy (pusher->fields.server->origin, pushorig);
1161 VectorCopy (pusher->fields.server->angles, pushang);
1162 pushltime = pusher->fields.server->ltime;
1164 // move the pusher to its final position
1166 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1167 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1168 pusher->fields.server->ltime += movetime;
1169 SV_LinkEdict (pusher, false);
1172 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1173 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1174 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);
1175 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1177 savesolid = pusher->fields.server->solid;
1179 // see if any solid entities are inside the final position
1182 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1183 for (e = 0;e < numcheckentities;e++)
1185 prvm_edict_t *check = checkentities[e];
1186 int checkcontents = SV_GenericHitSuperContentsMask(check);
1187 if (check->fields.server->movetype == MOVETYPE_NONE
1188 || check->fields.server->movetype == MOVETYPE_PUSH
1189 || check->fields.server->movetype == MOVETYPE_FOLLOW
1190 || check->fields.server->movetype == MOVETYPE_NOCLIP
1191 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1194 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1195 check->priv.server->waterposition_forceupdate = true;
1197 // if the entity is standing on the pusher, it will definitely be moved
1198 // if the entity is not standing on the pusher, but is in the pusher's
1199 // final position, move it
1200 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1202 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1203 if (!trace.startsolid)
1208 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1211 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1212 org2[0] = DotProduct (org, forward);
1213 org2[1] = DotProduct (org, left);
1214 org2[2] = DotProduct (org, up);
1215 VectorSubtract (org2, org, move);
1216 VectorAdd (move, move1, move);
1219 VectorCopy (move1, move);
1221 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1222 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1223 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1225 // try moving the contacted entity
1226 pusher->fields.server->solid = SOLID_NOT;
1227 trace = SV_PushEntity (check, move, true);
1228 // FIXME: turn players specially
1229 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1230 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1231 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1233 // this trace.fraction < 1 check causes items to fall off of pushers
1234 // if they pass under or through a wall
1235 // the groundentity check causes items to fall off of ledges
1236 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1237 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1239 // if it is still inside the pusher, block
1240 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1241 if (trace.startsolid)
1243 // try moving the contacted entity a tiny bit further to account for precision errors
1245 pusher->fields.server->solid = SOLID_NOT;
1246 VectorScale(move, 1.1, move2);
1247 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1248 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1249 SV_PushEntity (check, move2, true);
1250 pusher->fields.server->solid = savesolid;
1251 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1252 if (trace.startsolid)
1254 // try moving the contacted entity a tiny bit less to account for precision errors
1255 pusher->fields.server->solid = SOLID_NOT;
1256 VectorScale(move, 0.9, move2);
1257 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1258 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1259 SV_PushEntity (check, move2, true);
1260 pusher->fields.server->solid = savesolid;
1261 Collision_ClipToGenericEntity(&trace, pushermodel, pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1262 if (trace.startsolid)
1264 // still inside pusher, so it's really blocked
1267 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1269 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1272 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1273 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1277 VectorCopy (pushorig, pusher->fields.server->origin);
1278 VectorCopy (pushang, pusher->fields.server->angles);
1279 pusher->fields.server->ltime = pushltime;
1280 SV_LinkEdict (pusher, false);
1282 // move back any entities we already moved
1283 for (i = 0;i < num_moved;i++)
1285 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1286 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1287 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1288 SV_LinkEdict (ed, false);
1291 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1292 if (pusher->fields.server->blocked)
1294 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1295 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1296 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1303 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1304 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1305 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1314 void SV_Physics_Pusher (prvm_edict_t *ent)
1316 float thinktime, oldltime, movetime;
1318 oldltime = ent->fields.server->ltime;
1320 thinktime = ent->fields.server->nextthink;
1321 if (thinktime < ent->fields.server->ltime + sv.frametime)
1323 movetime = thinktime - ent->fields.server->ltime;
1328 movetime = sv.frametime;
1331 // advances ent->fields.server->ltime if not blocked
1332 SV_PushMove (ent, movetime);
1334 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1336 ent->fields.server->nextthink = 0;
1337 prog->globals.server->time = sv.time;
1338 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1339 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1340 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1346 ===============================================================================
1350 ===============================================================================
1353 static float unstickoffsets[] =
1387 This is a big hack to try and fix the rare case of getting stuck in the world
1391 void SV_CheckStuck (prvm_edict_t *ent)
1396 if (!SV_TestEntityPosition(ent, vec3_origin))
1398 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1402 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1404 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1406 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]);
1407 SV_LinkEdict (ent, true);
1412 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1413 if (!SV_TestEntityPosition(ent, offset))
1415 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1416 SV_LinkEdict (ent, true);
1420 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1423 static void SV_UnstickEntity (prvm_edict_t *ent)
1427 // if not stuck in a bmodel, just return
1428 if (!SV_TestEntityPosition(ent, vec3_origin))
1431 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1433 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1435 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]);
1436 SV_LinkEdict (ent, true);
1441 if (developer.integer >= 100)
1442 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1451 qboolean SV_CheckWater (prvm_edict_t *ent)
1454 int nNativeContents;
1457 point[0] = ent->fields.server->origin[0];
1458 point[1] = ent->fields.server->origin[1];
1459 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1461 // DRESK - Support for Entity Contents Transition Event
1462 // NOTE: Some logic needed to be slightly re-ordered
1463 // to not affect performance and allow for the feature.
1465 // Acquire Super Contents Prior to Resets
1466 cont = SV_PointSuperContents(point);
1467 // Acquire Native Contents Here
1468 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1470 // DRESK - Support for Entity Contents Transition Event
1471 if(ent->fields.server->watertype)
1472 // Entity did NOT Spawn; Check
1473 SV_CheckContentsTransition(ent, nNativeContents);
1476 ent->fields.server->waterlevel = 0;
1477 ent->fields.server->watertype = CONTENTS_EMPTY;
1478 cont = SV_PointSuperContents(point);
1479 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1481 ent->fields.server->watertype = nNativeContents;
1482 ent->fields.server->waterlevel = 1;
1483 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1484 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1486 ent->fields.server->waterlevel = 2;
1487 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1488 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1489 ent->fields.server->waterlevel = 3;
1493 return ent->fields.server->waterlevel > 1;
1502 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1505 vec3_t forward, into, side;
1507 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1508 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1510 // cut the tangential velocity
1511 i = DotProduct (stepnormal, ent->fields.server->velocity);
1512 VectorScale (stepnormal, i, into);
1513 VectorSubtract (ent->fields.server->velocity, into, side);
1514 ent->fields.server->velocity[0] = side[0] * (1 + d);
1515 ent->fields.server->velocity[1] = side[1] * (1 + d);
1521 =====================
1524 Player has come to a dead stop, possibly due to the problem with limited
1525 float precision at some angle joins in the BSP hull.
1527 Try fixing by pushing one pixel in each direction.
1529 This is a hack, but in the interest of good gameplay...
1530 ======================
1532 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1537 VectorCopy (ent->fields.server->origin, oldorg);
1540 for (i=0 ; i<8 ; i++)
1542 // try pushing a little in an axial direction
1545 case 0: dir[0] = 2; dir[1] = 0; break;
1546 case 1: dir[0] = 0; dir[1] = 2; break;
1547 case 2: dir[0] = -2; dir[1] = 0; break;
1548 case 3: dir[0] = 0; dir[1] = -2; break;
1549 case 4: dir[0] = 2; dir[1] = 2; break;
1550 case 5: dir[0] = -2; dir[1] = 2; break;
1551 case 6: dir[0] = 2; dir[1] = -2; break;
1552 case 7: dir[0] = -2; dir[1] = -2; break;
1555 SV_PushEntity (ent, dir, false);
1557 // retry the original move
1558 ent->fields.server->velocity[0] = oldvel[0];
1559 ent->fields.server->velocity[1] = oldvel[1];
1560 ent->fields.server->velocity[2] = 0;
1561 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1563 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1564 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1566 Con_DPrint("TryUnstick - success.\n");
1570 // go back to the original pos and try again
1571 VectorCopy (oldorg, ent->fields.server->origin);
1575 VectorClear (ent->fields.server->velocity);
1576 Con_DPrint("TryUnstick - failure.\n");
1582 =====================
1585 Only used by players
1586 ======================
1588 void SV_WalkMove (prvm_edict_t *ent)
1590 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1591 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1594 // if frametime is 0 (due to client sending the same timestamp twice),
1596 if (sv.frametime <= 0)
1599 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1601 SV_CheckVelocity(ent);
1603 // do a regular slide move unless it looks like you ran into a step
1604 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1606 VectorCopy (ent->fields.server->origin, start_origin);
1607 VectorCopy (ent->fields.server->velocity, start_velocity);
1609 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1611 // if the move did not hit the ground at any point, we're not on ground
1613 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1615 SV_CheckVelocity(ent);
1617 VectorCopy(ent->fields.server->origin, originalmove_origin);
1618 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1619 originalmove_clip = clip;
1620 originalmove_flags = (int)ent->fields.server->flags;
1621 originalmove_groundentity = ent->fields.server->groundentity;
1623 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1626 if (sv_nostep.integer)
1629 // if move didn't block on a step, return
1632 // if move was not trying to move into the step, return
1633 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1636 if (ent->fields.server->movetype != MOVETYPE_FLY)
1638 // return if gibbed by a trigger
1639 if (ent->fields.server->movetype != MOVETYPE_WALK)
1642 // only step up while jumping if that is enabled
1643 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1644 if (!oldonground && ent->fields.server->waterlevel == 0)
1648 // try moving up and forward to go up a step
1649 // back to start pos
1650 VectorCopy (start_origin, ent->fields.server->origin);
1651 VectorCopy (start_velocity, ent->fields.server->velocity);
1654 VectorClear (upmove);
1655 upmove[2] = sv_stepheight.value;
1656 // FIXME: don't link?
1657 SV_PushEntity(ent, upmove, false);
1660 ent->fields.server->velocity[2] = 0;
1661 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1662 ent->fields.server->velocity[2] += start_velocity[2];
1664 SV_CheckVelocity(ent);
1666 // check for stuckness, possibly due to the limited precision of floats
1667 // in the clipping hulls
1669 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1670 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1672 //Con_Printf("wall\n");
1673 // stepping up didn't make any progress, revert to original move
1674 VectorCopy(originalmove_origin, ent->fields.server->origin);
1675 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1676 //clip = originalmove_clip;
1677 ent->fields.server->flags = originalmove_flags;
1678 ent->fields.server->groundentity = originalmove_groundentity;
1679 // now try to unstick if needed
1680 //clip = SV_TryUnstick (ent, oldvel);
1684 //Con_Printf("step - ");
1686 // extra friction based on view angle
1687 if (clip & 2 && sv_wallfriction.integer)
1688 SV_WallFriction (ent, stepnormal);
1690 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1691 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))
1695 VectorClear (downmove);
1696 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1697 // FIXME: don't link?
1698 downtrace = SV_PushEntity (ent, downmove, false);
1700 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1702 // this has been disabled so that you can't jump when you are stepping
1703 // up while already jumping (also known as the Quake2 double jump bug)
1705 // LordHavoc: disabled this check so you can walk on monsters/players
1706 //if (ent->fields.server->solid == SOLID_BSP)
1708 //Con_Printf("onground\n");
1709 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1710 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1716 //Con_Printf("slope\n");
1717 // if the push down didn't end up on good ground, use the move without
1718 // the step up. This happens near wall / slope combinations, and can
1719 // cause the player to hop up higher on a slope too steep to climb
1720 VectorCopy(originalmove_origin, ent->fields.server->origin);
1721 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1722 //clip = originalmove_clip;
1723 ent->fields.server->flags = originalmove_flags;
1724 ent->fields.server->groundentity = originalmove_groundentity;
1727 SV_CheckVelocity(ent);
1730 //============================================================================
1736 Entities that are "stuck" to another entity
1739 void SV_Physics_Follow (prvm_edict_t *ent)
1741 vec3_t vf, vr, vu, angles, v;
1745 if (!SV_RunThink (ent))
1748 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1749 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1750 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])
1752 // quick case for no rotation
1753 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1757 angles[0] = -ent->fields.server->punchangle[0];
1758 angles[1] = ent->fields.server->punchangle[1];
1759 angles[2] = ent->fields.server->punchangle[2];
1760 AngleVectors (angles, vf, vr, vu);
1761 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];
1762 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];
1763 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];
1764 angles[0] = -e->fields.server->angles[0];
1765 angles[1] = e->fields.server->angles[1];
1766 angles[2] = e->fields.server->angles[2];
1767 AngleVectors (angles, vf, vr, vu);
1768 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1769 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1770 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1772 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1773 SV_LinkEdict (ent, true);
1777 ==============================================================================
1781 ==============================================================================
1786 SV_CheckWaterTransition
1790 void SV_CheckWaterTransition (prvm_edict_t *ent)
1793 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1794 if (!ent->fields.server->watertype)
1796 // just spawned here
1797 ent->fields.server->watertype = cont;
1798 ent->fields.server->waterlevel = 1;
1802 // DRESK - Support for Entity Contents Transition Event
1803 // NOTE: Call here BEFORE updating the watertype below,
1804 // and suppress watersplash sound if a valid function
1805 // call was made to allow for custom "splash" sounds.
1806 if( !SV_CheckContentsTransition(ent, cont) )
1807 { // Contents Transition Function Invalid; Potentially Play Water Sound
1808 // check if the entity crossed into or out of water
1809 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1810 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1813 if (cont <= CONTENTS_WATER)
1815 ent->fields.server->watertype = cont;
1816 ent->fields.server->waterlevel = 1;
1820 ent->fields.server->watertype = CONTENTS_EMPTY;
1821 ent->fields.server->waterlevel = 0;
1829 Toss, bounce, and fly movement. When onground, do nothing.
1832 void SV_Physics_Toss (prvm_edict_t *ent)
1837 // if onground, return without moving
1838 if ((int)ent->fields.server->flags & FL_ONGROUND)
1840 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1842 // don't stick to ground if onground and moving upward
1843 ent->fields.server->flags -= FL_ONGROUND;
1845 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1847 // we can trust FL_ONGROUND if groundentity is world because it never moves
1850 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1852 // if ent was supported by a brush model on previous frame,
1853 // and groundentity is now freed, set groundentity to 0 (world)
1854 // which leaves it suspended in the air
1855 ent->fields.server->groundentity = 0;
1859 ent->priv.server->suspendedinairflag = false;
1861 SV_CheckVelocity (ent);
1864 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1865 SV_AddGravity (ent);
1868 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1871 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1872 trace = SV_PushEntity (ent, move, true);
1873 if (ent->priv.server->free)
1875 if (trace.bmodelstartsolid)
1877 // try to unstick the entity
1878 SV_UnstickEntity(ent);
1879 trace = SV_PushEntity (ent, move, false);
1880 if (ent->priv.server->free)
1884 if (trace.fraction < 1)
1886 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1888 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1889 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1891 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1894 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1895 // LordHavoc: fixed grenades not bouncing when fired down a slope
1896 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1898 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1899 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1901 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1902 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1903 VectorClear (ent->fields.server->velocity);
1904 VectorClear (ent->fields.server->avelocity);
1907 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1911 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1913 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1914 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1915 VectorClear (ent->fields.server->velocity);
1916 VectorClear (ent->fields.server->avelocity);
1919 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1924 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1925 if (trace.plane.normal[2] > 0.7)
1927 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1928 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1929 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1930 ent->priv.server->suspendedinairflag = true;
1931 VectorClear (ent->fields.server->velocity);
1932 VectorClear (ent->fields.server->avelocity);
1935 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1939 // check for in water
1940 SV_CheckWaterTransition (ent);
1944 ===============================================================================
1948 ===============================================================================
1955 Monsters freefall when they don't have a ground entity, otherwise
1956 all movement is done with discrete steps.
1958 This is also used for objects that have become still on the ground, but
1959 will fall if the floor is pulled out from under them.
1962 void SV_Physics_Step (prvm_edict_t *ent)
1964 int flags = (int)ent->fields.server->flags;
1965 // don't fall at all if fly/swim
1966 if (!(flags & (FL_FLY | FL_SWIM)))
1968 if (flags & FL_ONGROUND)
1970 // freefall if onground and moving upward
1971 // freefall if not standing on a world surface (it may be a lift or trap door)
1972 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1974 ent->fields.server->flags -= FL_ONGROUND;
1976 SV_CheckVelocity(ent);
1977 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1978 SV_LinkEdict(ent, true);
1979 ent->priv.server->waterposition_forceupdate = true;
1984 // freefall if not onground
1985 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1988 SV_CheckVelocity(ent);
1989 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
1990 SV_LinkEdict(ent, true);
1993 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1994 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1995 ent->priv.server->waterposition_forceupdate = true;
2000 if (!SV_RunThink(ent))
2003 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2005 ent->priv.server->waterposition_forceupdate = false;
2006 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2007 SV_CheckWaterTransition(ent);
2011 //============================================================================
2013 static void SV_Physics_Entity (prvm_edict_t *ent)
2015 // don't run a move on newly spawned projectiles as it messes up movement
2016 // interpolation and rocket trails
2017 qboolean runmove = ent->priv.server->move;
2018 ent->priv.server->move = true;
2019 switch ((int) ent->fields.server->movetype)
2022 case MOVETYPE_FAKEPUSH:
2023 SV_Physics_Pusher (ent);
2026 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2027 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2030 case MOVETYPE_FOLLOW:
2031 SV_Physics_Follow (ent);
2033 case MOVETYPE_NOCLIP:
2034 if (SV_RunThink(ent))
2037 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2038 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2040 SV_LinkEdict(ent, false);
2043 SV_Physics_Step (ent);
2046 if (SV_RunThink (ent))
2048 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2049 SV_AddGravity (ent);
2050 SV_CheckStuck (ent);
2052 SV_LinkEdict (ent, true);
2056 case MOVETYPE_BOUNCE:
2057 case MOVETYPE_BOUNCEMISSILE:
2058 case MOVETYPE_FLYMISSILE:
2061 if (SV_RunThink (ent) && (runmove || !sv_gameplayfix_delayprojectiles.integer))
2062 SV_Physics_Toss (ent);
2065 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2070 void SV_Physics_ClientMove(void)
2073 ent = host_client->edict;
2075 // call player physics, this needs the proper frametime
2076 prog->globals.server->frametime = sv.frametime;
2079 // call standard client pre-think, with frametime = 0
2080 prog->globals.server->time = sv.time;
2081 prog->globals.server->frametime = 0;
2082 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2083 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2084 prog->globals.server->frametime = sv.frametime;
2086 // make sure the velocity is sane (not a NaN)
2087 SV_CheckVelocity(ent);
2088 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2089 // player_run/player_stand1 does not horribly malfunction if the
2090 // velocity becomes a number that is both == 0 and != 0
2091 // (sounds to me like NaN but to be absolutely safe...)
2092 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2093 VectorClear(ent->fields.server->velocity);
2095 // perform MOVETYPE_WALK behavior
2096 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2097 SV_AddGravity (ent);
2098 SV_CheckStuck (ent);
2101 SV_CheckVelocity (ent);
2103 SV_LinkEdict (ent, true);
2105 SV_CheckVelocity (ent);
2107 // call standard player post-think, with frametime = 0
2108 prog->globals.server->time = sv.time;
2109 prog->globals.server->frametime = 0;
2110 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2111 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2112 prog->globals.server->frametime = sv.frametime;
2114 if(ent->fields.server->fixangle)
2116 // angle fixing was requested by physics code...
2117 // so store the current angles for later use
2118 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2119 host_client->fixangle_angles_set = TRUE;
2121 // and clear fixangle for the next frame
2122 ent->fields.server->fixangle = 0;
2126 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2128 // don't do physics on disconnected clients, FrikBot relies on this
2129 if (!host_client->spawned)
2131 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2135 // don't run physics here if running asynchronously
2136 if (host_client->clmovement_skipphysicsframes <= 0)
2139 // make sure the velocity is sane (not a NaN)
2140 SV_CheckVelocity(ent);
2141 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2142 // player_run/player_stand1 does not horribly malfunction if the
2143 // velocity becomes a number that is both == 0 and != 0
2144 // (sounds to me like NaN but to be absolutely safe...)
2145 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2146 VectorClear(ent->fields.server->velocity);
2148 // call standard client pre-think
2149 prog->globals.server->time = sv.time;
2150 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2151 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2152 SV_CheckVelocity (ent);
2154 switch ((int) ent->fields.server->movetype)
2157 case MOVETYPE_FAKEPUSH:
2158 SV_Physics_Pusher (ent);
2161 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2162 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2165 case MOVETYPE_FOLLOW:
2166 SV_Physics_Follow (ent);
2168 case MOVETYPE_NOCLIP:
2171 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2172 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2175 SV_Physics_Step (ent);
2179 // don't run physics here if running asynchronously
2180 if (host_client->clmovement_skipphysicsframes <= 0)
2182 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2183 SV_AddGravity (ent);
2184 SV_CheckStuck (ent);
2189 case MOVETYPE_BOUNCE:
2190 case MOVETYPE_BOUNCEMISSILE:
2191 case MOVETYPE_FLYMISSILE:
2194 SV_Physics_Toss (ent);
2198 SV_CheckWater (ent);
2202 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2206 // decrement the countdown variable used to decide when to go back to
2207 // synchronous physics
2208 if (host_client->clmovement_skipphysicsframes > 0)
2209 host_client->clmovement_skipphysicsframes--;
2211 SV_CheckVelocity (ent);
2213 SV_LinkEdict (ent, true);
2215 SV_CheckVelocity (ent);
2217 // call standard player post-think
2218 prog->globals.server->time = sv.time;
2219 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2220 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2222 if(ent->fields.server->fixangle)
2224 // angle fixing was requested by physics code...
2225 // so store the current angles for later use
2226 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2227 host_client->fixangle_angles_set = TRUE;
2229 // and clear fixangle for the next frame
2230 ent->fields.server->fixangle = 0;
2240 void SV_Physics (void)
2245 // let the progs know that a new frame has started
2246 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2247 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2248 prog->globals.server->time = sv.time;
2249 prog->globals.server->frametime = sv.frametime;
2250 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2253 // treat each object in turn
2256 // if force_retouch, relink all the entities
2257 if (prog->globals.server->force_retouch > 0)
2258 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2259 if (!ent->priv.server->free)
2260 SV_LinkEdict (ent, true); // force retouch even for stationary
2262 // run physics on the client entities
2263 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2264 if (!ent->priv.server->free)
2265 SV_Physics_ClientEntity(ent);
2267 // run physics on all the non-client entities
2268 if (!sv_freezenonclients.integer)
2269 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2270 if (!ent->priv.server->free)
2271 SV_Physics_Entity(ent);
2273 if (prog->globals.server->force_retouch > 0)
2274 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2276 // LordHavoc: endframe support
2277 if (prog->funcoffsets.EndFrame)
2279 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2280 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2281 prog->globals.server->time = sv.time;
2282 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2285 // decrement prog->num_edicts if the highest number entities died
2286 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2288 if (!sv_freezenonclients.integer)
2289 sv.time += sv.frametime;