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, (int) 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, (int) 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)
524 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
525 VectorCopy(trace.endpos, ent->fields.server->origin);
527 // verify if the endpos is REALLY outside solid
528 VectorCopy(trace.endpos, org);
529 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
531 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
533 VectorCopy(org, ent->fields.server->origin);
544 void SV_CheckAllEnts (void)
549 // see if any solid entities are inside the final position
550 check = PRVM_NEXT_EDICT(prog->edicts);
551 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
553 if (check->priv.server->free)
555 if (check->fields.server->movetype == MOVETYPE_PUSH
556 || check->fields.server->movetype == MOVETYPE_NONE
557 || check->fields.server->movetype == MOVETYPE_FOLLOW
558 || check->fields.server->movetype == MOVETYPE_NOCLIP)
561 if (SV_TestEntityPosition (check, vec3_origin))
562 Con_Print("entity in invalid position\n");
566 // DRESK - Support for Entity Contents Transition Event
569 SV_CheckContentsTransition
571 returns true if entity had a valid contentstransition function call
574 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
576 int bValidFunctionCall;
577 prvm_eval_t *contentstransition;
579 // Default Valid Function Call to False
580 bValidFunctionCall = false;
582 if(ent->fields.server->watertype != nContents)
583 { // Changed Contents
584 // Acquire Contents Transition Function from QC
585 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
587 if(contentstransition->function)
588 { // Valid Function; Execute
589 // Assign Valid Function
590 bValidFunctionCall = true;
591 // Prepare Parameters (Original Contents, New Contents)
593 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
595 PRVM_G_FLOAT(OFS_PARM1) = nContents;
597 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
598 // Execute VM Function
599 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
603 // Return if Function Call was Valid
604 return bValidFunctionCall;
613 void SV_CheckVelocity (prvm_edict_t *ent)
621 for (i=0 ; i<3 ; i++)
623 if (IS_NAN(ent->fields.server->velocity[i]))
625 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
626 ent->fields.server->velocity[i] = 0;
628 if (IS_NAN(ent->fields.server->origin[i]))
630 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
631 ent->fields.server->origin[i] = 0;
635 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
636 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
637 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
639 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
640 ent->fields.server->velocity[0] *= wishspeed;
641 ent->fields.server->velocity[1] *= wishspeed;
642 ent->fields.server->velocity[2] *= wishspeed;
650 Runs thinking code if time. There is some play in the exact time the think
651 function will be called, because it is called before any movement is done
652 in a frame. Not used for pushmove objects, because they must be exact.
653 Returns false if the entity removed itself.
656 qboolean SV_RunThink (prvm_edict_t *ent)
660 // don't let things stay in the past.
661 // it is possible to start that way by a trigger with a local time.
662 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
665 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
667 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
668 ent->fields.server->nextthink = 0;
669 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
670 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
671 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
672 // mods often set nextthink to time to cause a think every frame,
673 // we don't want to loop in that case, so exit if the new nextthink is
674 // <= the time the qc was told, also exit if it is past the end of the
676 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
679 return !ent->priv.server->free;
686 Two entities have touched, so run their touch functions
689 extern void VM_SetTraceGlobals(const trace_t *trace);
690 extern sizebuf_t vm_tempstringsbuf;
691 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
693 int restorevm_tempstringsbuf_cursize;
694 int old_self, old_other;
695 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
698 old_self = prog->globals.server->self;
699 old_other = prog->globals.server->other;
700 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
702 VM_SetTraceGlobals(trace);
704 prog->globals.server->time = sv.time;
705 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
707 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
708 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
709 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
712 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
714 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
715 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
716 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
717 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
718 prog->globals.server->trace_plane_dist = -trace->plane.dist;
719 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
720 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
722 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
724 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
726 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
728 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
731 prog->globals.server->self = old_self;
732 prog->globals.server->other = old_other;
733 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
741 Slide off of the impacting object
742 returns the blocked flags (1 = floor, 2 = step / wall)
745 #define STOP_EPSILON 0.1
746 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
751 backoff = -DotProduct (in, normal) * overbounce;
752 VectorMA(in, backoff, normal, out);
754 for (i = 0;i < 3;i++)
755 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
764 The basic solid body movement clip that slides along multiple planes
765 Returns the clipflags if the velocity was modified (hit something solid)
769 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
772 static float SV_Gravity (prvm_edict_t *ent);
773 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
774 // LordHavoc: increased from 5 to 32
775 #define MAX_CLIP_PLANES 32
776 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
778 int blocked, bumpcount;
780 float d, time_left, gravity;
781 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
791 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
793 gravity = SV_Gravity(ent) * 0.5f;
794 ent->fields.server->velocity[2] -= gravity;
798 applygravity = false;
799 ent->fields.server->velocity[2] -= SV_Gravity(ent);
803 VectorCopy(ent->fields.server->velocity, original_velocity);
804 VectorCopy(ent->fields.server->velocity, primal_velocity);
807 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
809 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
812 VectorScale(ent->fields.server->velocity, time_left, push);
814 VectorAdd(ent->fields.server->origin, push, end);
816 trace = SV_PushEntity(ent, push, false, false); // the caller calls SV_LinkEntity on the own later
818 if(!VectorCompare(trace.endpos, ent->fields.server->origin))
820 // we got teleported by a touch function
821 // let's abort the move
822 Con_DPrintf("we got teleported\n");
827 //if (trace.fraction < 0.002)
832 VectorCopy(ent->fields.server->origin, start);
833 start[2] += 3;//0.03125;
834 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
835 end[2] += 3;//0.03125;
836 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
837 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)))
839 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
845 for (i = 0;i < numplanes;i++)
847 VectorCopy(ent->fields.server->origin, start);
848 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
849 VectorMA(start, 3, planes[i], start);
850 VectorMA(end, 3, planes[i], end);
851 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
852 if (trace.fraction < testtrace.fraction)
855 VectorCopy(start, ent->fields.server->origin);
860 // VectorAdd(ent->fields.server->origin, planes[j], start);
866 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);
867 if (trace.fraction < 1)
868 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
873 if (trace.bmodelstartsolid)
875 // LordHavoc: note: this code is what makes entities stick in place
876 // if embedded in world only (you can walk through other objects if
878 // entity is trapped in another solid
879 VectorClear(ent->fields.server->velocity);
884 if (trace.fraction == 1)
886 if (trace.plane.normal[2])
888 if (trace.plane.normal[2] > 0.7)
895 Con_Printf ("SV_FlyMove: !trace.ent");
896 trace.ent = prog->edicts;
899 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
900 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
907 // save the trace for player extrafriction
909 VectorCopy(trace.plane.normal, stepnormal);
911 if (trace.fraction >= 0.001)
913 // actually covered some distance
914 VectorCopy(ent->fields.server->velocity, original_velocity);
918 time_left *= 1 - trace.fraction;
920 // clipped to another plane
921 if (numplanes >= MAX_CLIP_PLANES)
923 // this shouldn't really happen
924 VectorClear(ent->fields.server->velocity);
930 for (i = 0;i < numplanes;i++)
931 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
935 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
940 VectorCopy(trace.plane.normal, planes[numplanes]);
943 if (sv_newflymove.integer)
944 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
947 // modify original_velocity so it parallels all of the clip planes
948 for (i = 0;i < numplanes;i++)
950 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
951 for (j = 0;j < numplanes;j++)
956 if (DotProduct(new_velocity, planes[j]) < 0)
966 // go along this plane
967 VectorCopy(new_velocity, ent->fields.server->velocity);
971 // go along the crease
974 VectorClear(ent->fields.server->velocity);
978 CrossProduct(planes[0], planes[1], dir);
979 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
980 VectorNormalize(dir);
981 d = DotProduct(dir, ent->fields.server->velocity);
982 VectorScale(dir, d, ent->fields.server->velocity);
986 // if current velocity is against the original velocity,
987 // stop dead to avoid tiny occilations in sloping corners
988 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
990 VectorClear(ent->fields.server->velocity);
995 //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]);
998 if ((blocked & 1) == 0 && bumpcount > 1)
1000 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1001 // flag ONGROUND if there's ground under it
1002 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1006 // LordHavoc: this came from QW and allows you to get out of water more easily
1007 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
1008 VectorCopy(primal_velocity, ent->fields.server->velocity);
1009 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1010 ent->fields.server->velocity[2] -= gravity;
1020 static float SV_Gravity (prvm_edict_t *ent)
1025 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1026 if (val!=0 && val->_float)
1027 ent_gravity = val->_float;
1030 return ent_gravity * sv_gravity.value * sv.frametime;
1035 ===============================================================================
1039 ===============================================================================
1046 Does not change the entities velocity at all
1049 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1056 VectorAdd (ent->fields.server->origin, push, end);
1058 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1059 type = MOVE_MISSILE;
1060 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1061 type = MOVE_NOMONSTERS; // only clip against bmodels
1065 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1066 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1069 VectorCopy (trace.endpos, ent->fields.server->origin);
1071 impact = (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)));
1075 SV_LinkEdict (ent, dolink);
1076 SV_Impact (ent, &trace);
1079 SV_LinkEdict (ent, true);
1091 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1094 int pusherowner, pusherprog;
1097 float savesolid, movetime2, pushltime;
1098 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1100 int numcheckentities;
1101 static prvm_edict_t *checkentities[MAX_EDICTS];
1102 dp_model_t *pushermodel;
1104 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1105 unsigned short moved_edicts[MAX_EDICTS];
1107 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])
1109 pusher->fields.server->ltime += movetime;
1113 switch ((int) pusher->fields.server->solid)
1115 // LordHavoc: valid pusher types
1118 case SOLID_SLIDEBOX:
1119 case SOLID_CORPSE: // LordHavoc: this would be weird...
1121 // LordHavoc: no collisions
1124 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1125 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1126 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1127 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1128 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1129 pusher->fields.server->ltime += movetime;
1130 SV_LinkEdict (pusher, false);
1133 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1136 index = (int) pusher->fields.server->modelindex;
1137 if (index < 1 || index >= MAX_MODELS)
1139 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1142 pushermodel = sv.models[index];
1143 pusherowner = pusher->fields.server->owner;
1144 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1146 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1148 movetime2 = movetime;
1149 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1150 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1151 if (moveangle[0] || moveangle[2])
1153 for (i = 0;i < 3;i++)
1157 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1158 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1162 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1163 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1167 else if (moveangle[1])
1169 for (i = 0;i < 3;i++)
1173 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1174 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1178 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1179 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1185 for (i = 0;i < 3;i++)
1189 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1190 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1194 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1195 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1200 VectorNegate (moveangle, a);
1201 AngleVectorsFLU (a, forward, left, up);
1203 VectorCopy (pusher->fields.server->origin, pushorig);
1204 VectorCopy (pusher->fields.server->angles, pushang);
1205 pushltime = pusher->fields.server->ltime;
1207 // move the pusher to its final position
1209 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1210 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1211 pusher->fields.server->ltime += movetime;
1212 SV_LinkEdict (pusher, false);
1215 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1216 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1217 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);
1218 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1220 savesolid = pusher->fields.server->solid;
1222 // see if any solid entities are inside the final position
1225 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1226 for (e = 0;e < numcheckentities;e++)
1228 prvm_edict_t *check = checkentities[e];
1229 if (check->fields.server->movetype == MOVETYPE_NONE
1230 || check->fields.server->movetype == MOVETYPE_PUSH
1231 || check->fields.server->movetype == MOVETYPE_FOLLOW
1232 || check->fields.server->movetype == MOVETYPE_NOCLIP
1233 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1236 if (check->fields.server->owner == pusherprog)
1239 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1242 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1244 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1245 check->priv.server->waterposition_forceupdate = true;
1247 checkcontents = SV_GenericHitSuperContentsMask(check);
1249 // if the entity is standing on the pusher, it will definitely be moved
1250 // if the entity is not standing on the pusher, but is in the pusher's
1251 // final position, move it
1252 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1254 Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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);
1255 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1256 if (!trace.startsolid)
1258 //Con_Printf("- not in solid\n");
1266 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1267 org2[0] = DotProduct (org, forward);
1268 org2[1] = DotProduct (org, left);
1269 org2[2] = DotProduct (org, up);
1270 VectorSubtract (org2, org, move);
1271 VectorAdd (move, move1, move);
1274 VectorCopy (move1, move);
1276 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1278 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1279 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1280 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1282 // try moving the contacted entity
1283 pusher->fields.server->solid = SOLID_NOT;
1284 trace = SV_PushEntity (check, move, true, true);
1285 // FIXME: turn players specially
1286 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1287 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1288 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1290 // this trace.fraction < 1 check causes items to fall off of pushers
1291 // if they pass under or through a wall
1292 // the groundentity check causes items to fall off of ledges
1293 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1294 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1296 // if it is still inside the pusher, block
1297 Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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);
1298 if (trace.startsolid)
1300 // try moving the contacted entity a tiny bit further to account for precision errors
1302 pusher->fields.server->solid = SOLID_NOT;
1303 VectorScale(move, 1.1, move2);
1304 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1305 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1306 SV_PushEntity (check, move2, true, true);
1307 pusher->fields.server->solid = savesolid;
1308 Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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);
1309 if (trace.startsolid)
1311 // try moving the contacted entity a tiny bit less to account for precision errors
1312 pusher->fields.server->solid = SOLID_NOT;
1313 VectorScale(move, 0.9, move2);
1314 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1315 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1316 SV_PushEntity (check, move2, true, true);
1317 pusher->fields.server->solid = savesolid;
1318 Collision_ClipToGenericEntity(&trace, pushermodel, (int) 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);
1319 if (trace.startsolid)
1321 // still inside pusher, so it's really blocked
1324 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1326 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1329 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1330 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1334 VectorCopy (pushorig, pusher->fields.server->origin);
1335 VectorCopy (pushang, pusher->fields.server->angles);
1336 pusher->fields.server->ltime = pushltime;
1337 SV_LinkEdict (pusher, false);
1339 // move back any entities we already moved
1340 for (i = 0;i < num_moved;i++)
1342 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1343 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1344 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1345 SV_LinkEdict (ed, false);
1348 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1349 if (pusher->fields.server->blocked)
1351 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1352 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1353 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1360 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1361 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1362 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1371 void SV_Physics_Pusher (prvm_edict_t *ent)
1373 float thinktime, oldltime, movetime;
1375 oldltime = ent->fields.server->ltime;
1377 thinktime = ent->fields.server->nextthink;
1378 if (thinktime < ent->fields.server->ltime + sv.frametime)
1380 movetime = thinktime - ent->fields.server->ltime;
1385 movetime = sv.frametime;
1388 // advances ent->fields.server->ltime if not blocked
1389 SV_PushMove (ent, movetime);
1391 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1393 ent->fields.server->nextthink = 0;
1394 prog->globals.server->time = sv.time;
1395 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1396 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1397 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1403 ===============================================================================
1407 ===============================================================================
1410 static float unstickoffsets[] =
1412 // poutting -/+z changes first as they are least weird
1427 typedef enum unstickresult_e
1435 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1439 // if not stuck in a bmodel, just return
1440 if (!SV_TestEntityPosition(ent, vec3_origin))
1441 return UNSTICK_GOOD;
1443 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1445 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1447 VectorCopy(unstickoffsets + i, offset);
1448 SV_LinkEdict (ent, true);
1449 return UNSTICK_UNSTUCK;
1453 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1454 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1456 for(i = 2; i <= maxunstick; ++i)
1458 VectorClear(offset);
1460 if (!SV_TestEntityPosition(ent, offset))
1462 SV_LinkEdict (ent, true);
1463 return UNSTICK_UNSTUCK;
1466 if (!SV_TestEntityPosition(ent, offset))
1468 SV_LinkEdict (ent, true);
1469 return UNSTICK_UNSTUCK;
1473 return UNSTICK_STUCK;
1476 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1479 switch(SV_UnstickEntityReturnOffset(ent, offset))
1483 case UNSTICK_UNSTUCK:
1484 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), offset[0], offset[1], offset[2]);
1487 if (developer.integer >= 100)
1488 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1491 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1500 This is a big hack to try and fix the rare case of getting stuck in the world
1504 void SV_CheckStuck (prvm_edict_t *ent)
1508 switch(SV_UnstickEntityReturnOffset(ent, offset))
1511 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1513 case UNSTICK_UNSTUCK:
1514 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), offset[0], offset[1], offset[2]);
1517 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1518 if (!SV_TestEntityPosition(ent, offset))
1520 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1521 SV_LinkEdict (ent, true);
1524 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1527 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1537 qboolean SV_CheckWater (prvm_edict_t *ent)
1540 int nNativeContents;
1543 point[0] = ent->fields.server->origin[0];
1544 point[1] = ent->fields.server->origin[1];
1545 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1547 // DRESK - Support for Entity Contents Transition Event
1548 // NOTE: Some logic needed to be slightly re-ordered
1549 // to not affect performance and allow for the feature.
1551 // Acquire Super Contents Prior to Resets
1552 cont = SV_PointSuperContents(point);
1553 // Acquire Native Contents Here
1554 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1556 // DRESK - Support for Entity Contents Transition Event
1557 if(ent->fields.server->watertype)
1558 // Entity did NOT Spawn; Check
1559 SV_CheckContentsTransition(ent, nNativeContents);
1562 ent->fields.server->waterlevel = 0;
1563 ent->fields.server->watertype = CONTENTS_EMPTY;
1564 cont = SV_PointSuperContents(point);
1565 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1567 ent->fields.server->watertype = nNativeContents;
1568 ent->fields.server->waterlevel = 1;
1569 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1570 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1572 ent->fields.server->waterlevel = 2;
1573 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1574 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1575 ent->fields.server->waterlevel = 3;
1579 return ent->fields.server->waterlevel > 1;
1588 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1591 vec3_t forward, into, side;
1593 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1594 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1596 // cut the tangential velocity
1597 i = DotProduct (stepnormal, ent->fields.server->velocity);
1598 VectorScale (stepnormal, i, into);
1599 VectorSubtract (ent->fields.server->velocity, into, side);
1600 ent->fields.server->velocity[0] = side[0] * (1 + d);
1601 ent->fields.server->velocity[1] = side[1] * (1 + d);
1607 =====================
1610 Player has come to a dead stop, possibly due to the problem with limited
1611 float precision at some angle joins in the BSP hull.
1613 Try fixing by pushing one pixel in each direction.
1615 This is a hack, but in the interest of good gameplay...
1616 ======================
1618 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1623 VectorCopy (ent->fields.server->origin, oldorg);
1626 for (i=0 ; i<8 ; i++)
1628 // try pushing a little in an axial direction
1631 case 0: dir[0] = 2; dir[1] = 0; break;
1632 case 1: dir[0] = 0; dir[1] = 2; break;
1633 case 2: dir[0] = -2; dir[1] = 0; break;
1634 case 3: dir[0] = 0; dir[1] = -2; break;
1635 case 4: dir[0] = 2; dir[1] = 2; break;
1636 case 5: dir[0] = -2; dir[1] = 2; break;
1637 case 6: dir[0] = 2; dir[1] = -2; break;
1638 case 7: dir[0] = -2; dir[1] = -2; break;
1641 SV_PushEntity (ent, dir, false, true);
1643 // retry the original move
1644 ent->fields.server->velocity[0] = oldvel[0];
1645 ent->fields.server->velocity[1] = oldvel[1];
1646 ent->fields.server->velocity[2] = 0;
1647 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1649 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1650 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1652 Con_DPrint("TryUnstick - success.\n");
1656 // go back to the original pos and try again
1657 VectorCopy (oldorg, ent->fields.server->origin);
1661 VectorClear (ent->fields.server->velocity);
1662 Con_DPrint("TryUnstick - failure.\n");
1668 =====================
1671 Only used by players
1672 ======================
1674 void SV_WalkMove (prvm_edict_t *ent)
1676 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1677 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1679 qboolean applygravity;
1681 // if frametime is 0 (due to client sending the same timestamp twice),
1683 if (sv.frametime <= 0)
1686 SV_CheckStuck (ent);
1688 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1690 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1692 SV_CheckVelocity(ent);
1694 // do a regular slide move unless it looks like you ran into a step
1695 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1697 VectorCopy (ent->fields.server->origin, start_origin);
1698 VectorCopy (ent->fields.server->velocity, start_velocity);
1700 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1702 // if the move did not hit the ground at any point, we're not on ground
1704 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1706 SV_CheckVelocity(ent);
1707 SV_LinkEdict (ent, true);
1709 VectorCopy(ent->fields.server->origin, originalmove_origin);
1710 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1711 originalmove_clip = clip;
1712 originalmove_flags = (int)ent->fields.server->flags;
1713 originalmove_groundentity = ent->fields.server->groundentity;
1715 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1718 if (sv_nostep.integer)
1721 // if move didn't block on a step, return
1724 // if move was not trying to move into the step, return
1725 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1728 if (ent->fields.server->movetype != MOVETYPE_FLY)
1730 // return if gibbed by a trigger
1731 if (ent->fields.server->movetype != MOVETYPE_WALK)
1734 // only step up while jumping if that is enabled
1735 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1736 if (!oldonground && ent->fields.server->waterlevel == 0)
1740 // try moving up and forward to go up a step
1741 // back to start pos
1742 VectorCopy (start_origin, ent->fields.server->origin);
1743 VectorCopy (start_velocity, ent->fields.server->velocity);
1746 VectorClear (upmove);
1747 upmove[2] = sv_stepheight.value;
1748 SV_PushEntity(ent, upmove, false, false);
1751 ent->fields.server->velocity[2] = 0;
1752 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1753 ent->fields.server->velocity[2] += start_velocity[2];
1755 SV_CheckVelocity(ent);
1756 SV_LinkEdict (ent, true);
1758 // check for stuckness, possibly due to the limited precision of floats
1759 // in the clipping hulls
1761 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1762 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1764 //Con_Printf("wall\n");
1765 // stepping up didn't make any progress, revert to original move
1766 VectorCopy(originalmove_origin, ent->fields.server->origin);
1767 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1768 //clip = originalmove_clip;
1769 ent->fields.server->flags = originalmove_flags;
1770 ent->fields.server->groundentity = originalmove_groundentity;
1771 // now try to unstick if needed
1772 //clip = SV_TryUnstick (ent, oldvel);
1776 //Con_Printf("step - ");
1778 // extra friction based on view angle
1779 if (clip & 2 && sv_wallfriction.integer)
1780 SV_WallFriction (ent, stepnormal);
1782 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1783 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))
1787 VectorClear (downmove);
1788 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1789 downtrace = SV_PushEntity (ent, downmove, false, false);
1791 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1793 // this has been disabled so that you can't jump when you are stepping
1794 // up while already jumping (also known as the Quake2 double jump bug)
1796 // LordHavoc: disabled this check so you can walk on monsters/players
1797 //if (ent->fields.server->solid == SOLID_BSP)
1799 //Con_Printf("onground\n");
1800 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1801 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1807 //Con_Printf("slope\n");
1808 // if the push down didn't end up on good ground, use the move without
1809 // the step up. This happens near wall / slope combinations, and can
1810 // cause the player to hop up higher on a slope too steep to climb
1811 VectorCopy(originalmove_origin, ent->fields.server->origin);
1812 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1813 //clip = originalmove_clip;
1814 ent->fields.server->flags = originalmove_flags;
1815 ent->fields.server->groundentity = originalmove_groundentity;
1818 SV_CheckVelocity(ent);
1819 SV_LinkEdict (ent, true);
1822 //============================================================================
1828 Entities that are "stuck" to another entity
1831 void SV_Physics_Follow (prvm_edict_t *ent)
1833 vec3_t vf, vr, vu, angles, v;
1837 if (!SV_RunThink (ent))
1840 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1841 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1842 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])
1844 // quick case for no rotation
1845 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1849 angles[0] = -ent->fields.server->punchangle[0];
1850 angles[1] = ent->fields.server->punchangle[1];
1851 angles[2] = ent->fields.server->punchangle[2];
1852 AngleVectors (angles, vf, vr, vu);
1853 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];
1854 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];
1855 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];
1856 angles[0] = -e->fields.server->angles[0];
1857 angles[1] = e->fields.server->angles[1];
1858 angles[2] = e->fields.server->angles[2];
1859 AngleVectors (angles, vf, vr, vu);
1860 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1861 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1862 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1864 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1865 SV_LinkEdict (ent, true);
1869 ==============================================================================
1873 ==============================================================================
1878 SV_CheckWaterTransition
1882 void SV_CheckWaterTransition (prvm_edict_t *ent)
1885 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1886 if (!ent->fields.server->watertype)
1888 // just spawned here
1889 ent->fields.server->watertype = cont;
1890 ent->fields.server->waterlevel = 1;
1894 // DRESK - Support for Entity Contents Transition Event
1895 // NOTE: Call here BEFORE updating the watertype below,
1896 // and suppress watersplash sound if a valid function
1897 // call was made to allow for custom "splash" sounds.
1898 if( !SV_CheckContentsTransition(ent, cont) )
1899 { // Contents Transition Function Invalid; Potentially Play Water Sound
1900 // check if the entity crossed into or out of water
1901 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1902 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1905 if (cont <= CONTENTS_WATER)
1907 ent->fields.server->watertype = cont;
1908 ent->fields.server->waterlevel = 1;
1912 ent->fields.server->watertype = CONTENTS_EMPTY;
1913 ent->fields.server->waterlevel = 0;
1921 Toss, bounce, and fly movement. When onground, do nothing.
1924 void SV_Physics_Toss (prvm_edict_t *ent)
1931 // if onground, return without moving
1932 if ((int)ent->fields.server->flags & FL_ONGROUND)
1934 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1936 // don't stick to ground if onground and moving upward
1937 ent->fields.server->flags -= FL_ONGROUND;
1939 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1941 // we can trust FL_ONGROUND if groundentity is world because it never moves
1944 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1946 // if ent was supported by a brush model on previous frame,
1947 // and groundentity is now freed, set groundentity to 0 (world)
1948 // which leaves it suspended in the air
1949 ent->fields.server->groundentity = 0;
1953 ent->priv.server->suspendedinairflag = false;
1955 SV_CheckVelocity (ent);
1958 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1959 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1962 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1964 movetime = sv.frametime;
1965 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
1968 VectorScale (ent->fields.server->velocity, movetime, move);
1969 trace = SV_PushEntity (ent, move, true, true);
1970 if (ent->priv.server->free)
1972 if (trace.bmodelstartsolid)
1974 // try to unstick the entity
1975 SV_UnstickEntity(ent);
1976 trace = SV_PushEntity (ent, move, false, true);
1977 if (ent->priv.server->free)
1980 if (trace.fraction == 1)
1982 movetime *= 1 - min(1, trace.fraction);
1983 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1985 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1986 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1988 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1990 float d, ent_gravity;
1992 float bouncefactor = 0.5f;
1993 float bouncestop = 60.0f / 800.0f;
1995 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
1996 if (val!=0 && val->_float)
1997 bouncefactor = val->_float;
1999 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2000 if (val!=0 && val->_float)
2001 bouncestop = val->_float;
2003 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2004 // LordHavoc: fixed grenades not bouncing when fired down a slope
2005 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2006 if (val!=0 && val->_float)
2007 ent_gravity = val->_float;
2010 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2012 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2013 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2015 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2016 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2017 VectorClear (ent->fields.server->velocity);
2018 VectorClear (ent->fields.server->avelocity);
2021 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2025 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2027 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2028 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2029 VectorClear (ent->fields.server->velocity);
2030 VectorClear (ent->fields.server->avelocity);
2033 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2038 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2039 if (trace.plane.normal[2] > 0.7)
2041 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2042 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2043 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2044 ent->priv.server->suspendedinairflag = true;
2045 VectorClear (ent->fields.server->velocity);
2046 VectorClear (ent->fields.server->avelocity);
2049 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2051 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2055 // check for in water
2056 SV_CheckWaterTransition (ent);
2060 ===============================================================================
2064 ===============================================================================
2071 Monsters freefall when they don't have a ground entity, otherwise
2072 all movement is done with discrete steps.
2074 This is also used for objects that have become still on the ground, but
2075 will fall if the floor is pulled out from under them.
2078 void SV_Physics_Step (prvm_edict_t *ent)
2080 int flags = (int)ent->fields.server->flags;
2083 // Backup Velocity in the event that movetypesteplandevent is called,
2084 // to provide a parameter with the entity's velocity at impact.
2085 prvm_eval_t *movetypesteplandevent;
2086 vec3_t backupVelocity;
2087 VectorCopy(ent->fields.server->velocity, backupVelocity);
2088 // don't fall at all if fly/swim
2089 if (!(flags & (FL_FLY | FL_SWIM)))
2091 if (flags & FL_ONGROUND)
2093 // freefall if onground and moving upward
2094 // freefall if not standing on a world surface (it may be a lift or trap door)
2095 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2097 ent->fields.server->flags -= FL_ONGROUND;
2098 SV_CheckVelocity(ent);
2099 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2100 SV_LinkEdict(ent, true);
2101 ent->priv.server->waterposition_forceupdate = true;
2106 // freefall if not onground
2107 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2109 SV_CheckVelocity(ent);
2110 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2111 SV_LinkEdict(ent, true);
2114 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2116 // DRESK - Check for Entity Land Event Function
2117 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2119 if(movetypesteplandevent->function)
2120 { // Valid Function; Execute
2121 // Prepare Parameters
2122 // Assign Velocity at Impact
2123 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2124 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2125 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2127 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2128 // Execute VM Function
2129 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2132 // Check for Engine Landing Sound
2133 if(sv_sound_land.string)
2134 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2136 ent->priv.server->waterposition_forceupdate = true;
2141 if (!SV_RunThink(ent))
2144 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2146 ent->priv.server->waterposition_forceupdate = false;
2147 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2148 SV_CheckWaterTransition(ent);
2152 //============================================================================
2154 static void SV_Physics_Entity (prvm_edict_t *ent)
2156 // don't run think/move on newly spawned projectiles as it messes up
2157 // movement interpolation and rocket trails, and is inconsistent with
2158 // respect to entities spawned in the same frame
2159 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2160 // but if it spawns a lower numbered ent, it doesn't - this never moves
2161 // ents in the first frame regardless)
2162 qboolean runmove = ent->priv.server->move;
2163 ent->priv.server->move = true;
2164 if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2166 switch ((int) ent->fields.server->movetype)
2169 case MOVETYPE_FAKEPUSH:
2170 SV_Physics_Pusher (ent);
2173 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2174 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2177 case MOVETYPE_FOLLOW:
2178 SV_Physics_Follow (ent);
2180 case MOVETYPE_NOCLIP:
2181 if (SV_RunThink(ent))
2184 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2185 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2187 SV_LinkEdict(ent, false);
2190 SV_Physics_Step (ent);
2193 if (SV_RunThink (ent))
2197 case MOVETYPE_BOUNCE:
2198 case MOVETYPE_BOUNCEMISSILE:
2199 case MOVETYPE_FLYMISSILE:
2202 if (SV_RunThink (ent))
2203 SV_Physics_Toss (ent);
2206 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2211 void SV_Physics_ClientMove(void)
2214 ent = host_client->edict;
2216 // call player physics, this needs the proper frametime
2217 prog->globals.server->frametime = sv.frametime;
2220 // call standard client pre-think, with frametime = 0
2221 prog->globals.server->time = sv.time;
2222 prog->globals.server->frametime = 0;
2223 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2224 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2225 prog->globals.server->frametime = sv.frametime;
2227 // make sure the velocity is sane (not a NaN)
2228 SV_CheckVelocity(ent);
2229 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2230 // player_run/player_stand1 does not horribly malfunction if the
2231 // velocity becomes a number that is both == 0 and != 0
2232 // (sounds to me like NaN but to be absolutely safe...)
2233 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2234 VectorClear(ent->fields.server->velocity);
2236 // perform MOVETYPE_WALK behavior
2239 // call standard player post-think, with frametime = 0
2240 prog->globals.server->time = sv.time;
2241 prog->globals.server->frametime = 0;
2242 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2243 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2244 prog->globals.server->frametime = sv.frametime;
2246 if(ent->fields.server->fixangle)
2248 // angle fixing was requested by physics code...
2249 // so store the current angles for later use
2250 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2251 host_client->fixangle_angles_set = TRUE;
2253 // and clear fixangle for the next frame
2254 ent->fields.server->fixangle = 0;
2258 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2260 // don't do physics on disconnected clients, FrikBot relies on this
2261 if (!host_client->spawned)
2263 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2267 // don't run physics here if running asynchronously
2268 if (host_client->clmovement_inputtimeout <= 0)
2271 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2274 // make sure the velocity is sane (not a NaN)
2275 SV_CheckVelocity(ent);
2276 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2277 // player_run/player_stand1 does not horribly malfunction if the
2278 // velocity becomes a number that is both == 0 and != 0
2279 // (sounds to me like NaN but to be absolutely safe...)
2280 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2281 VectorClear(ent->fields.server->velocity);
2283 // call standard client pre-think
2284 prog->globals.server->time = sv.time;
2285 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2286 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2287 SV_CheckVelocity (ent);
2289 switch ((int) ent->fields.server->movetype)
2292 case MOVETYPE_FAKEPUSH:
2293 SV_Physics_Pusher (ent);
2296 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2297 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2300 case MOVETYPE_FOLLOW:
2301 SV_Physics_Follow (ent);
2303 case MOVETYPE_NOCLIP:
2306 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2307 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2310 SV_Physics_Step (ent);
2314 // don't run physics here if running asynchronously
2315 if (host_client->clmovement_inputtimeout <= 0)
2319 case MOVETYPE_BOUNCE:
2320 case MOVETYPE_BOUNCEMISSILE:
2321 case MOVETYPE_FLYMISSILE:
2324 SV_Physics_Toss (ent);
2331 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2335 // decrement the countdown variable used to decide when to go back to
2336 // synchronous physics
2337 if (host_client->clmovement_inputtimeout > sv.frametime)
2338 host_client->clmovement_inputtimeout -= sv.frametime;
2340 host_client->clmovement_inputtimeout = 0;
2342 SV_CheckVelocity (ent);
2344 SV_LinkEdict (ent, true);
2346 SV_CheckVelocity (ent);
2348 // call standard player post-think
2349 prog->globals.server->time = sv.time;
2350 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2351 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2353 if(ent->fields.server->fixangle)
2355 // angle fixing was requested by physics code...
2356 // so store the current angles for later use
2357 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2358 host_client->fixangle_angles_set = TRUE;
2360 // and clear fixangle for the next frame
2361 ent->fields.server->fixangle = 0;
2371 void SV_Physics (void)
2376 // let the progs know that a new frame has started
2377 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2378 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2379 prog->globals.server->time = sv.time;
2380 prog->globals.server->frametime = sv.frametime;
2381 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2384 // treat each object in turn
2387 // if force_retouch, relink all the entities
2388 if (prog->globals.server->force_retouch > 0)
2389 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2390 if (!ent->priv.server->free)
2391 SV_LinkEdict (ent, true); // force retouch even for stationary
2393 // run physics on the client entities
2394 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2395 if (!ent->priv.server->free)
2396 SV_Physics_ClientEntity(ent);
2398 // run physics on all the non-client entities
2399 if (!sv_freezenonclients.integer)
2400 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2401 if (!ent->priv.server->free)
2402 SV_Physics_Entity(ent);
2404 if (prog->globals.server->force_retouch > 0)
2405 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2407 // LordHavoc: endframe support
2408 if (prog->funcoffsets.EndFrame)
2410 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2411 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2412 prog->globals.server->time = sv.time;
2413 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2416 // decrement prog->num_edicts if the highest number entities died
2417 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2419 if (!sv_freezenonclients.integer)
2420 sv.time += sv.frametime;