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 int pusherowner, pusherprog;
1061 float savesolid, movetime2, pushltime;
1062 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1064 int numcheckentities;
1065 static prvm_edict_t *checkentities[MAX_EDICTS];
1066 dp_model_t *pushermodel;
1068 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1069 unsigned short moved_edicts[MAX_EDICTS];
1071 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])
1073 pusher->fields.server->ltime += movetime;
1077 switch ((int) pusher->fields.server->solid)
1079 // LordHavoc: valid pusher types
1082 case SOLID_SLIDEBOX:
1083 case SOLID_CORPSE: // LordHavoc: this would be weird...
1085 // LordHavoc: no collisions
1088 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1089 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1090 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1091 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1092 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1093 pusher->fields.server->ltime += movetime;
1094 SV_LinkEdict (pusher, false);
1097 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1100 index = (int) pusher->fields.server->modelindex;
1101 if (index < 1 || index >= MAX_MODELS)
1103 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1106 pushermodel = sv.models[index];
1107 pusherowner = pusher->fields.server->owner;
1108 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1110 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1112 movetime2 = movetime;
1113 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1114 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1115 if (moveangle[0] || moveangle[2])
1117 for (i = 0;i < 3;i++)
1121 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1122 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1126 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1127 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1131 else if (moveangle[1])
1133 for (i = 0;i < 3;i++)
1137 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1138 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1142 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1143 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1149 for (i = 0;i < 3;i++)
1153 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1154 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1158 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1159 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1164 VectorNegate (moveangle, a);
1165 AngleVectorsFLU (a, forward, left, up);
1167 VectorCopy (pusher->fields.server->origin, pushorig);
1168 VectorCopy (pusher->fields.server->angles, pushang);
1169 pushltime = pusher->fields.server->ltime;
1171 // move the pusher to its final position
1173 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1174 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1175 pusher->fields.server->ltime += movetime;
1176 SV_LinkEdict (pusher, false);
1179 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1180 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1181 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);
1182 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1184 savesolid = pusher->fields.server->solid;
1186 // see if any solid entities are inside the final position
1189 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1190 for (e = 0;e < numcheckentities;e++)
1192 prvm_edict_t *check = checkentities[e];
1193 if (check->fields.server->movetype == MOVETYPE_NONE
1194 || check->fields.server->movetype == MOVETYPE_PUSH
1195 || check->fields.server->movetype == MOVETYPE_FOLLOW
1196 || check->fields.server->movetype == MOVETYPE_NOCLIP
1197 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1200 if (check->fields.server->owner == pusherprog)
1203 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1206 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1208 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1209 check->priv.server->waterposition_forceupdate = true;
1211 checkcontents = SV_GenericHitSuperContentsMask(check);
1213 // if the entity is standing on the pusher, it will definitely be moved
1214 // if the entity is not standing on the pusher, but is in the pusher's
1215 // final position, move it
1216 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1218 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);
1219 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1220 if (!trace.startsolid)
1222 //Con_Printf("- not in solid\n");
1230 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1231 org2[0] = DotProduct (org, forward);
1232 org2[1] = DotProduct (org, left);
1233 org2[2] = DotProduct (org, up);
1234 VectorSubtract (org2, org, move);
1235 VectorAdd (move, move1, move);
1238 VectorCopy (move1, move);
1240 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1242 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1243 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1244 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1246 // try moving the contacted entity
1247 pusher->fields.server->solid = SOLID_NOT;
1248 trace = SV_PushEntity (check, move, true);
1249 // FIXME: turn players specially
1250 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1251 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1252 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1254 // this trace.fraction < 1 check causes items to fall off of pushers
1255 // if they pass under or through a wall
1256 // the groundentity check causes items to fall off of ledges
1257 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1258 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1260 // if it is still inside the pusher, block
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 // try moving the contacted entity a tiny bit further to account for precision errors
1266 pusher->fields.server->solid = SOLID_NOT;
1267 VectorScale(move, 1.1, move2);
1268 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1269 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1270 SV_PushEntity (check, move2, true);
1271 pusher->fields.server->solid = savesolid;
1272 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);
1273 if (trace.startsolid)
1275 // try moving the contacted entity a tiny bit less to account for precision errors
1276 pusher->fields.server->solid = SOLID_NOT;
1277 VectorScale(move, 0.9, move2);
1278 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1279 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1280 SV_PushEntity (check, move2, true);
1281 pusher->fields.server->solid = savesolid;
1282 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);
1283 if (trace.startsolid)
1285 // still inside pusher, so it's really blocked
1288 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1290 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1293 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1294 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1298 VectorCopy (pushorig, pusher->fields.server->origin);
1299 VectorCopy (pushang, pusher->fields.server->angles);
1300 pusher->fields.server->ltime = pushltime;
1301 SV_LinkEdict (pusher, false);
1303 // move back any entities we already moved
1304 for (i = 0;i < num_moved;i++)
1306 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1307 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1308 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1309 SV_LinkEdict (ed, false);
1312 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1313 if (pusher->fields.server->blocked)
1315 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1316 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1317 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1324 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1325 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1326 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1335 void SV_Physics_Pusher (prvm_edict_t *ent)
1337 float thinktime, oldltime, movetime;
1339 oldltime = ent->fields.server->ltime;
1341 thinktime = ent->fields.server->nextthink;
1342 if (thinktime < ent->fields.server->ltime + sv.frametime)
1344 movetime = thinktime - ent->fields.server->ltime;
1349 movetime = sv.frametime;
1352 // advances ent->fields.server->ltime if not blocked
1353 SV_PushMove (ent, movetime);
1355 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1357 ent->fields.server->nextthink = 0;
1358 prog->globals.server->time = sv.time;
1359 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1360 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1361 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1367 ===============================================================================
1371 ===============================================================================
1374 static float unstickoffsets[] =
1424 This is a big hack to try and fix the rare case of getting stuck in the world
1428 void SV_CheckStuck (prvm_edict_t *ent)
1433 if (!SV_TestEntityPosition(ent, vec3_origin))
1435 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1439 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1441 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1443 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]);
1444 SV_LinkEdict (ent, true);
1449 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1450 if (!SV_TestEntityPosition(ent, offset))
1452 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1453 SV_LinkEdict (ent, true);
1457 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1460 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1464 // if not stuck in a bmodel, just return
1465 if (!SV_TestEntityPosition(ent, vec3_origin))
1468 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1470 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1472 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]);
1473 SV_LinkEdict (ent, true);
1478 if (developer.integer >= 100)
1479 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1489 qboolean SV_CheckWater (prvm_edict_t *ent)
1492 int nNativeContents;
1495 point[0] = ent->fields.server->origin[0];
1496 point[1] = ent->fields.server->origin[1];
1497 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1499 // DRESK - Support for Entity Contents Transition Event
1500 // NOTE: Some logic needed to be slightly re-ordered
1501 // to not affect performance and allow for the feature.
1503 // Acquire Super Contents Prior to Resets
1504 cont = SV_PointSuperContents(point);
1505 // Acquire Native Contents Here
1506 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1508 // DRESK - Support for Entity Contents Transition Event
1509 if(ent->fields.server->watertype)
1510 // Entity did NOT Spawn; Check
1511 SV_CheckContentsTransition(ent, nNativeContents);
1514 ent->fields.server->waterlevel = 0;
1515 ent->fields.server->watertype = CONTENTS_EMPTY;
1516 cont = SV_PointSuperContents(point);
1517 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1519 ent->fields.server->watertype = nNativeContents;
1520 ent->fields.server->waterlevel = 1;
1521 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1522 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1524 ent->fields.server->waterlevel = 2;
1525 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1526 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1527 ent->fields.server->waterlevel = 3;
1531 return ent->fields.server->waterlevel > 1;
1540 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1543 vec3_t forward, into, side;
1545 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1546 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1548 // cut the tangential velocity
1549 i = DotProduct (stepnormal, ent->fields.server->velocity);
1550 VectorScale (stepnormal, i, into);
1551 VectorSubtract (ent->fields.server->velocity, into, side);
1552 ent->fields.server->velocity[0] = side[0] * (1 + d);
1553 ent->fields.server->velocity[1] = side[1] * (1 + d);
1559 =====================
1562 Player has come to a dead stop, possibly due to the problem with limited
1563 float precision at some angle joins in the BSP hull.
1565 Try fixing by pushing one pixel in each direction.
1567 This is a hack, but in the interest of good gameplay...
1568 ======================
1570 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1575 VectorCopy (ent->fields.server->origin, oldorg);
1578 for (i=0 ; i<8 ; i++)
1580 // try pushing a little in an axial direction
1583 case 0: dir[0] = 2; dir[1] = 0; break;
1584 case 1: dir[0] = 0; dir[1] = 2; break;
1585 case 2: dir[0] = -2; dir[1] = 0; break;
1586 case 3: dir[0] = 0; dir[1] = -2; break;
1587 case 4: dir[0] = 2; dir[1] = 2; break;
1588 case 5: dir[0] = -2; dir[1] = 2; break;
1589 case 6: dir[0] = 2; dir[1] = -2; break;
1590 case 7: dir[0] = -2; dir[1] = -2; break;
1593 SV_PushEntity (ent, dir, false);
1595 // retry the original move
1596 ent->fields.server->velocity[0] = oldvel[0];
1597 ent->fields.server->velocity[1] = oldvel[1];
1598 ent->fields.server->velocity[2] = 0;
1599 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1601 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1602 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1604 Con_DPrint("TryUnstick - success.\n");
1608 // go back to the original pos and try again
1609 VectorCopy (oldorg, ent->fields.server->origin);
1613 VectorClear (ent->fields.server->velocity);
1614 Con_DPrint("TryUnstick - failure.\n");
1620 =====================
1623 Only used by players
1624 ======================
1626 void SV_WalkMove (prvm_edict_t *ent)
1628 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1629 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1632 // if frametime is 0 (due to client sending the same timestamp twice),
1634 if (sv.frametime <= 0)
1637 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1639 SV_CheckVelocity(ent);
1641 // do a regular slide move unless it looks like you ran into a step
1642 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1644 VectorCopy (ent->fields.server->origin, start_origin);
1645 VectorCopy (ent->fields.server->velocity, start_velocity);
1647 clip = SV_FlyMove (ent, sv.frametime, NULL, hitsupercontentsmask);
1649 // if the move did not hit the ground at any point, we're not on ground
1651 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1653 SV_CheckVelocity(ent);
1655 VectorCopy(ent->fields.server->origin, originalmove_origin);
1656 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1657 originalmove_clip = clip;
1658 originalmove_flags = (int)ent->fields.server->flags;
1659 originalmove_groundentity = ent->fields.server->groundentity;
1661 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1664 if (sv_nostep.integer)
1667 // if move didn't block on a step, return
1670 // if move was not trying to move into the step, return
1671 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1674 if (ent->fields.server->movetype != MOVETYPE_FLY)
1676 // return if gibbed by a trigger
1677 if (ent->fields.server->movetype != MOVETYPE_WALK)
1680 // only step up while jumping if that is enabled
1681 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1682 if (!oldonground && ent->fields.server->waterlevel == 0)
1686 // try moving up and forward to go up a step
1687 // back to start pos
1688 VectorCopy (start_origin, ent->fields.server->origin);
1689 VectorCopy (start_velocity, ent->fields.server->velocity);
1692 VectorClear (upmove);
1693 upmove[2] = sv_stepheight.value;
1694 // FIXME: don't link?
1695 SV_PushEntity(ent, upmove, false);
1698 ent->fields.server->velocity[2] = 0;
1699 clip = SV_FlyMove (ent, sv.frametime, stepnormal, hitsupercontentsmask);
1700 ent->fields.server->velocity[2] += start_velocity[2];
1702 SV_CheckVelocity(ent);
1704 // check for stuckness, possibly due to the limited precision of floats
1705 // in the clipping hulls
1707 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1708 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1710 //Con_Printf("wall\n");
1711 // stepping up didn't make any progress, revert to original move
1712 VectorCopy(originalmove_origin, ent->fields.server->origin);
1713 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1714 //clip = originalmove_clip;
1715 ent->fields.server->flags = originalmove_flags;
1716 ent->fields.server->groundentity = originalmove_groundentity;
1717 // now try to unstick if needed
1718 //clip = SV_TryUnstick (ent, oldvel);
1722 //Con_Printf("step - ");
1724 // extra friction based on view angle
1725 if (clip & 2 && sv_wallfriction.integer)
1726 SV_WallFriction (ent, stepnormal);
1728 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1729 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))
1733 VectorClear (downmove);
1734 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1735 // FIXME: don't link?
1736 downtrace = SV_PushEntity (ent, downmove, false);
1738 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1740 // this has been disabled so that you can't jump when you are stepping
1741 // up while already jumping (also known as the Quake2 double jump bug)
1743 // LordHavoc: disabled this check so you can walk on monsters/players
1744 //if (ent->fields.server->solid == SOLID_BSP)
1746 //Con_Printf("onground\n");
1747 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1748 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1754 //Con_Printf("slope\n");
1755 // if the push down didn't end up on good ground, use the move without
1756 // the step up. This happens near wall / slope combinations, and can
1757 // cause the player to hop up higher on a slope too steep to climb
1758 VectorCopy(originalmove_origin, ent->fields.server->origin);
1759 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1760 //clip = originalmove_clip;
1761 ent->fields.server->flags = originalmove_flags;
1762 ent->fields.server->groundentity = originalmove_groundentity;
1765 SV_CheckVelocity(ent);
1768 //============================================================================
1774 Entities that are "stuck" to another entity
1777 void SV_Physics_Follow (prvm_edict_t *ent)
1779 vec3_t vf, vr, vu, angles, v;
1783 if (!SV_RunThink (ent))
1786 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1787 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1788 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])
1790 // quick case for no rotation
1791 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1795 angles[0] = -ent->fields.server->punchangle[0];
1796 angles[1] = ent->fields.server->punchangle[1];
1797 angles[2] = ent->fields.server->punchangle[2];
1798 AngleVectors (angles, vf, vr, vu);
1799 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];
1800 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];
1801 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];
1802 angles[0] = -e->fields.server->angles[0];
1803 angles[1] = e->fields.server->angles[1];
1804 angles[2] = e->fields.server->angles[2];
1805 AngleVectors (angles, vf, vr, vu);
1806 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1807 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1808 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1810 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1811 SV_LinkEdict (ent, true);
1815 ==============================================================================
1819 ==============================================================================
1824 SV_CheckWaterTransition
1828 void SV_CheckWaterTransition (prvm_edict_t *ent)
1831 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1832 if (!ent->fields.server->watertype)
1834 // just spawned here
1835 ent->fields.server->watertype = cont;
1836 ent->fields.server->waterlevel = 1;
1840 // DRESK - Support for Entity Contents Transition Event
1841 // NOTE: Call here BEFORE updating the watertype below,
1842 // and suppress watersplash sound if a valid function
1843 // call was made to allow for custom "splash" sounds.
1844 if( !SV_CheckContentsTransition(ent, cont) )
1845 { // Contents Transition Function Invalid; Potentially Play Water Sound
1846 // check if the entity crossed into or out of water
1847 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1848 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1851 if (cont <= CONTENTS_WATER)
1853 ent->fields.server->watertype = cont;
1854 ent->fields.server->waterlevel = 1;
1858 ent->fields.server->watertype = CONTENTS_EMPTY;
1859 ent->fields.server->waterlevel = 0;
1867 Toss, bounce, and fly movement. When onground, do nothing.
1870 void SV_Physics_Toss (prvm_edict_t *ent)
1875 // if onground, return without moving
1876 if ((int)ent->fields.server->flags & FL_ONGROUND)
1878 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1880 // don't stick to ground if onground and moving upward
1881 ent->fields.server->flags -= FL_ONGROUND;
1883 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1885 // we can trust FL_ONGROUND if groundentity is world because it never moves
1888 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1890 // if ent was supported by a brush model on previous frame,
1891 // and groundentity is now freed, set groundentity to 0 (world)
1892 // which leaves it suspended in the air
1893 ent->fields.server->groundentity = 0;
1897 ent->priv.server->suspendedinairflag = false;
1899 SV_CheckVelocity (ent);
1902 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1903 SV_AddGravity (ent);
1906 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1909 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1910 trace = SV_PushEntity (ent, move, true);
1911 if (ent->priv.server->free)
1913 if (trace.bmodelstartsolid)
1915 // try to unstick the entity
1916 SV_UnstickEntity(ent);
1917 trace = SV_PushEntity (ent, move, false);
1918 if (ent->priv.server->free)
1922 if (trace.fraction < 1)
1924 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1926 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1927 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1929 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1932 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1933 // LordHavoc: fixed grenades not bouncing when fired down a slope
1934 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1936 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1937 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1939 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1940 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1941 VectorClear (ent->fields.server->velocity);
1942 VectorClear (ent->fields.server->avelocity);
1945 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1949 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1951 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1952 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1953 VectorClear (ent->fields.server->velocity);
1954 VectorClear (ent->fields.server->avelocity);
1957 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1962 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1963 if (trace.plane.normal[2] > 0.7)
1965 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1966 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1967 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1968 ent->priv.server->suspendedinairflag = true;
1969 VectorClear (ent->fields.server->velocity);
1970 VectorClear (ent->fields.server->avelocity);
1973 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1977 // check for in water
1978 SV_CheckWaterTransition (ent);
1982 ===============================================================================
1986 ===============================================================================
1993 Monsters freefall when they don't have a ground entity, otherwise
1994 all movement is done with discrete steps.
1996 This is also used for objects that have become still on the ground, but
1997 will fall if the floor is pulled out from under them.
2000 void SV_Physics_Step (prvm_edict_t *ent)
2002 int flags = (int)ent->fields.server->flags;
2005 // Backup Velocity in the event that movetypesteplandevent is called,
2006 // to provide a parameter with the entity's velocity at impact.
2007 prvm_eval_t *movetypesteplandevent;
2008 vec3_t backupVelocity;
2009 VectorCopy(ent->fields.server->velocity, backupVelocity);
2010 // don't fall at all if fly/swim
2011 if (!(flags & (FL_FLY | FL_SWIM)))
2013 if (flags & FL_ONGROUND)
2015 // freefall if onground and moving upward
2016 // freefall if not standing on a world surface (it may be a lift or trap door)
2017 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2019 ent->fields.server->flags -= FL_ONGROUND;
2021 SV_CheckVelocity(ent);
2022 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
2023 SV_LinkEdict(ent, true);
2024 ent->priv.server->waterposition_forceupdate = true;
2029 // freefall if not onground
2030 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2033 SV_CheckVelocity(ent);
2034 SV_FlyMove(ent, sv.frametime, NULL, SV_GenericHitSuperContentsMask(ent));
2035 SV_LinkEdict(ent, true);
2038 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2040 // DRESK - Check for Entity Land Event Function
2041 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2043 if(movetypesteplandevent->function)
2044 { // Valid Function; Execute
2045 // Prepare Parameters
2046 // Assign Velocity at Impact
2047 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2048 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2049 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2051 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2052 // Execute VM Function
2053 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2056 // Check for Engine Landing Sound
2057 if(sv_sound_land.string)
2058 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2060 ent->priv.server->waterposition_forceupdate = true;
2065 if (!SV_RunThink(ent))
2068 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2070 ent->priv.server->waterposition_forceupdate = false;
2071 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2072 SV_CheckWaterTransition(ent);
2076 //============================================================================
2078 static void SV_Physics_Entity (prvm_edict_t *ent)
2080 // don't run a move on newly spawned projectiles as it messes up movement
2081 // interpolation and rocket trails
2082 qboolean runmove = ent->priv.server->move;
2083 ent->priv.server->move = true;
2084 switch ((int) ent->fields.server->movetype)
2087 case MOVETYPE_FAKEPUSH:
2088 SV_Physics_Pusher (ent);
2091 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2092 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2095 case MOVETYPE_FOLLOW:
2096 SV_Physics_Follow (ent);
2098 case MOVETYPE_NOCLIP:
2099 if (SV_RunThink(ent))
2102 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2103 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2105 SV_LinkEdict(ent, false);
2108 SV_Physics_Step (ent);
2111 if (SV_RunThink (ent))
2113 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2114 SV_AddGravity (ent);
2115 SV_CheckStuck (ent);
2117 SV_LinkEdict (ent, true);
2121 case MOVETYPE_BOUNCE:
2122 case MOVETYPE_BOUNCEMISSILE:
2123 case MOVETYPE_FLYMISSILE:
2126 if (SV_RunThink (ent) && (runmove || !sv_gameplayfix_delayprojectiles.integer))
2127 SV_Physics_Toss (ent);
2130 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2135 void SV_Physics_ClientMove(void)
2138 ent = host_client->edict;
2140 // call player physics, this needs the proper frametime
2141 prog->globals.server->frametime = sv.frametime;
2144 // call standard client pre-think, with frametime = 0
2145 prog->globals.server->time = sv.time;
2146 prog->globals.server->frametime = 0;
2147 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2148 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2149 prog->globals.server->frametime = sv.frametime;
2151 // make sure the velocity is sane (not a NaN)
2152 SV_CheckVelocity(ent);
2153 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2154 // player_run/player_stand1 does not horribly malfunction if the
2155 // velocity becomes a number that is both == 0 and != 0
2156 // (sounds to me like NaN but to be absolutely safe...)
2157 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2158 VectorClear(ent->fields.server->velocity);
2160 // perform MOVETYPE_WALK behavior
2161 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2162 SV_AddGravity (ent);
2163 SV_CheckStuck (ent);
2166 SV_CheckVelocity (ent);
2168 SV_LinkEdict (ent, true);
2170 SV_CheckVelocity (ent);
2172 // call standard player post-think, with frametime = 0
2173 prog->globals.server->time = sv.time;
2174 prog->globals.server->frametime = 0;
2175 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2176 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2177 prog->globals.server->frametime = sv.frametime;
2179 if(ent->fields.server->fixangle)
2181 // angle fixing was requested by physics code...
2182 // so store the current angles for later use
2183 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2184 host_client->fixangle_angles_set = TRUE;
2186 // and clear fixangle for the next frame
2187 ent->fields.server->fixangle = 0;
2191 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2193 // don't do physics on disconnected clients, FrikBot relies on this
2194 if (!host_client->spawned)
2196 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2200 // don't run physics here if running asynchronously
2201 if (host_client->clmovement_skipphysicsframes <= 0)
2204 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2207 // make sure the velocity is sane (not a NaN)
2208 SV_CheckVelocity(ent);
2209 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2210 // player_run/player_stand1 does not horribly malfunction if the
2211 // velocity becomes a number that is both == 0 and != 0
2212 // (sounds to me like NaN but to be absolutely safe...)
2213 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2214 VectorClear(ent->fields.server->velocity);
2216 // call standard client pre-think
2217 prog->globals.server->time = sv.time;
2218 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2219 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2220 SV_CheckVelocity (ent);
2222 switch ((int) ent->fields.server->movetype)
2225 case MOVETYPE_FAKEPUSH:
2226 SV_Physics_Pusher (ent);
2229 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2230 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2233 case MOVETYPE_FOLLOW:
2234 SV_Physics_Follow (ent);
2236 case MOVETYPE_NOCLIP:
2239 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2240 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2243 SV_Physics_Step (ent);
2247 // don't run physics here if running asynchronously
2248 if (host_client->clmovement_skipphysicsframes <= 0)
2250 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2251 SV_AddGravity (ent);
2252 SV_CheckStuck (ent);
2257 case MOVETYPE_BOUNCE:
2258 case MOVETYPE_BOUNCEMISSILE:
2259 case MOVETYPE_FLYMISSILE:
2262 SV_Physics_Toss (ent);
2266 SV_CheckWater (ent);
2270 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2274 // decrement the countdown variable used to decide when to go back to
2275 // synchronous physics
2276 if (host_client->clmovement_skipphysicsframes > 0)
2277 host_client->clmovement_skipphysicsframes--;
2279 SV_CheckVelocity (ent);
2281 SV_LinkEdict (ent, true);
2283 SV_CheckVelocity (ent);
2285 // call standard player post-think
2286 prog->globals.server->time = sv.time;
2287 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2288 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2290 if(ent->fields.server->fixangle)
2292 // angle fixing was requested by physics code...
2293 // so store the current angles for later use
2294 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2295 host_client->fixangle_angles_set = TRUE;
2297 // and clear fixangle for the next frame
2298 ent->fields.server->fixangle = 0;
2308 void SV_Physics (void)
2313 // let the progs know that a new frame has started
2314 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2315 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2316 prog->globals.server->time = sv.time;
2317 prog->globals.server->frametime = sv.frametime;
2318 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2321 // treat each object in turn
2324 // if force_retouch, relink all the entities
2325 if (prog->globals.server->force_retouch > 0)
2326 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2327 if (!ent->priv.server->free)
2328 SV_LinkEdict (ent, true); // force retouch even for stationary
2330 // run physics on the client entities
2331 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2332 if (!ent->priv.server->free)
2333 SV_Physics_ClientEntity(ent);
2335 // run physics on all the non-client entities
2336 if (!sv_freezenonclients.integer)
2337 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2338 if (!ent->priv.server->free)
2339 SV_Physics_Entity(ent);
2341 if (prog->globals.server->force_retouch > 0)
2342 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2344 // LordHavoc: endframe support
2345 if (prog->funcoffsets.EndFrame)
2347 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2348 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2349 prog->globals.server->time = sv.time;
2350 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2353 // decrement prog->num_edicts if the highest number entities died
2354 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2356 if (!sv_freezenonclients.integer)
2357 sv.time += sv.frametime;