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 // LordHavoc: increased from 5 to 32
774 #define MAX_CLIP_PLANES 32
775 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
777 int blocked, bumpcount;
778 int i, j, impact, numplanes;
779 float d, time_left, gravity;
780 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
787 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
789 gravity = SV_Gravity(ent) * 0.5f;
790 ent->fields.server->velocity[2] -= gravity;
794 applygravity = false;
795 ent->fields.server->velocity[2] -= SV_Gravity(ent);
799 VectorCopy(ent->fields.server->velocity, original_velocity);
800 VectorCopy(ent->fields.server->velocity, primal_velocity);
803 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
805 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
808 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
809 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
811 //if (trace.fraction < 0.002)
816 VectorCopy(ent->fields.server->origin, start);
817 start[2] += 3;//0.03125;
818 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
819 end[2] += 3;//0.03125;
820 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
821 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)))
823 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
829 for (i = 0;i < numplanes;i++)
831 VectorCopy(ent->fields.server->origin, start);
832 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
833 VectorMA(start, 3, planes[i], start);
834 VectorMA(end, 3, planes[i], end);
835 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
836 if (trace.fraction < testtrace.fraction)
839 VectorCopy(start, ent->fields.server->origin);
844 // VectorAdd(ent->fields.server->origin, planes[j], start);
850 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);
851 if (trace.fraction < 1)
852 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
857 if (trace.bmodelstartsolid)
859 // LordHavoc: note: this code is what makes entities stick in place
860 // if embedded in world only (you can walk through other objects if
862 // entity is trapped in another solid
863 VectorClear(ent->fields.server->velocity);
868 // break if it moved the entire distance
869 if (trace.fraction == 1)
871 VectorCopy(trace.endpos, ent->fields.server->origin);
877 Con_Printf ("SV_FlyMove: !trace.ent");
878 trace.ent = prog->edicts;
881 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
883 if (trace.plane.normal[2])
885 if (trace.plane.normal[2] > 0.7)
889 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
890 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
897 // save the trace for player extrafriction
899 VectorCopy(trace.plane.normal, stepnormal);
902 if (trace.fraction >= 0.001)
904 // actually covered some distance
905 VectorCopy(trace.endpos, ent->fields.server->origin);
906 VectorCopy(ent->fields.server->velocity, original_velocity);
910 // run the impact function
913 SV_Impact(ent, &trace);
915 // break if removed by the impact function
916 if (ent->priv.server->free)
920 time_left *= 1 - trace.fraction;
922 // clipped to another plane
923 if (numplanes >= MAX_CLIP_PLANES)
925 // this shouldn't really happen
926 VectorClear(ent->fields.server->velocity);
932 for (i = 0;i < numplanes;i++)
933 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
937 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
942 VectorCopy(trace.plane.normal, planes[numplanes]);
945 if (sv_newflymove.integer)
946 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
949 // modify original_velocity so it parallels all of the clip planes
950 for (i = 0;i < numplanes;i++)
952 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
953 for (j = 0;j < numplanes;j++)
958 if (DotProduct(new_velocity, planes[j]) < 0)
968 // go along this plane
969 VectorCopy(new_velocity, ent->fields.server->velocity);
973 // go along the crease
976 VectorClear(ent->fields.server->velocity);
980 CrossProduct(planes[0], planes[1], dir);
981 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
982 VectorNormalize(dir);
983 d = DotProduct(dir, ent->fields.server->velocity);
984 VectorScale(dir, d, ent->fields.server->velocity);
988 // if current velocity is against the original velocity,
989 // stop dead to avoid tiny occilations in sloping corners
990 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
992 VectorClear(ent->fields.server->velocity);
997 //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]);
1000 if ((blocked & 1) == 0 && bumpcount > 1)
1002 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1003 // flag ONGROUND if there's ground under it
1004 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1008 // LordHavoc: this came from QW and allows you to get out of water more easily
1009 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
1010 VectorCopy(primal_velocity, ent->fields.server->velocity);
1011 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1012 ent->fields.server->velocity[2] -= gravity;
1022 static float SV_Gravity (prvm_edict_t *ent)
1027 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1028 if (val!=0 && val->_float)
1029 ent_gravity = val->_float;
1032 return ent_gravity * sv_gravity.value * sv.frametime;
1037 ===============================================================================
1041 ===============================================================================
1048 Does not change the entities velocity at all
1051 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
1057 VectorAdd (ent->fields.server->origin, push, end);
1059 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1060 type = MOVE_MISSILE;
1061 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1062 type = MOVE_NOMONSTERS; // only clip against bmodels
1066 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1067 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1070 VectorCopy (trace.endpos, ent->fields.server->origin);
1071 SV_LinkEdict (ent, true);
1073 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)))
1074 SV_Impact (ent, &trace);
1085 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1088 int pusherowner, pusherprog;
1091 float savesolid, movetime2, pushltime;
1092 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1094 int numcheckentities;
1095 static prvm_edict_t *checkentities[MAX_EDICTS];
1096 dp_model_t *pushermodel;
1098 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1099 unsigned short moved_edicts[MAX_EDICTS];
1101 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])
1103 pusher->fields.server->ltime += movetime;
1107 switch ((int) pusher->fields.server->solid)
1109 // LordHavoc: valid pusher types
1112 case SOLID_SLIDEBOX:
1113 case SOLID_CORPSE: // LordHavoc: this would be weird...
1115 // LordHavoc: no collisions
1118 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1119 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1120 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1121 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1122 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1123 pusher->fields.server->ltime += movetime;
1124 SV_LinkEdict (pusher, false);
1127 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1130 index = (int) pusher->fields.server->modelindex;
1131 if (index < 1 || index >= MAX_MODELS)
1133 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1136 pushermodel = sv.models[index];
1137 pusherowner = pusher->fields.server->owner;
1138 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1140 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1142 movetime2 = movetime;
1143 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1144 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1145 if (moveangle[0] || moveangle[2])
1147 for (i = 0;i < 3;i++)
1151 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1152 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1156 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1157 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1161 else if (moveangle[1])
1163 for (i = 0;i < 3;i++)
1167 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1168 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1172 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1173 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1179 for (i = 0;i < 3;i++)
1183 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1184 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1188 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1189 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1194 VectorNegate (moveangle, a);
1195 AngleVectorsFLU (a, forward, left, up);
1197 VectorCopy (pusher->fields.server->origin, pushorig);
1198 VectorCopy (pusher->fields.server->angles, pushang);
1199 pushltime = pusher->fields.server->ltime;
1201 // move the pusher to its final position
1203 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1204 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1205 pusher->fields.server->ltime += movetime;
1206 SV_LinkEdict (pusher, false);
1209 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1210 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1211 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);
1212 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1214 savesolid = pusher->fields.server->solid;
1216 // see if any solid entities are inside the final position
1219 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1220 for (e = 0;e < numcheckentities;e++)
1222 prvm_edict_t *check = checkentities[e];
1223 if (check->fields.server->movetype == MOVETYPE_NONE
1224 || check->fields.server->movetype == MOVETYPE_PUSH
1225 || check->fields.server->movetype == MOVETYPE_FOLLOW
1226 || check->fields.server->movetype == MOVETYPE_NOCLIP
1227 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1230 if (check->fields.server->owner == pusherprog)
1233 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1236 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1238 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1239 check->priv.server->waterposition_forceupdate = true;
1241 checkcontents = SV_GenericHitSuperContentsMask(check);
1243 // if the entity is standing on the pusher, it will definitely be moved
1244 // if the entity is not standing on the pusher, but is in the pusher's
1245 // final position, move it
1246 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1248 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);
1249 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1250 if (!trace.startsolid)
1252 //Con_Printf("- not in solid\n");
1260 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1261 org2[0] = DotProduct (org, forward);
1262 org2[1] = DotProduct (org, left);
1263 org2[2] = DotProduct (org, up);
1264 VectorSubtract (org2, org, move);
1265 VectorAdd (move, move1, move);
1268 VectorCopy (move1, move);
1270 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1272 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1273 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1274 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1276 // try moving the contacted entity
1277 pusher->fields.server->solid = SOLID_NOT;
1278 trace = SV_PushEntity (check, move, true);
1279 // FIXME: turn players specially
1280 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1281 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1282 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1284 // this trace.fraction < 1 check causes items to fall off of pushers
1285 // if they pass under or through a wall
1286 // the groundentity check causes items to fall off of ledges
1287 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1288 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1290 // if it is still inside the pusher, block
1291 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);
1292 if (trace.startsolid)
1294 // try moving the contacted entity a tiny bit further to account for precision errors
1296 pusher->fields.server->solid = SOLID_NOT;
1297 VectorScale(move, 1.1, move2);
1298 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1299 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1300 SV_PushEntity (check, move2, true);
1301 pusher->fields.server->solid = savesolid;
1302 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);
1303 if (trace.startsolid)
1305 // try moving the contacted entity a tiny bit less to account for precision errors
1306 pusher->fields.server->solid = SOLID_NOT;
1307 VectorScale(move, 0.9, move2);
1308 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1309 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1310 SV_PushEntity (check, move2, true);
1311 pusher->fields.server->solid = savesolid;
1312 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);
1313 if (trace.startsolid)
1315 // still inside pusher, so it's really blocked
1318 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1320 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1323 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1324 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1328 VectorCopy (pushorig, pusher->fields.server->origin);
1329 VectorCopy (pushang, pusher->fields.server->angles);
1330 pusher->fields.server->ltime = pushltime;
1331 SV_LinkEdict (pusher, false);
1333 // move back any entities we already moved
1334 for (i = 0;i < num_moved;i++)
1336 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1337 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1338 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1339 SV_LinkEdict (ed, false);
1342 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1343 if (pusher->fields.server->blocked)
1345 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1346 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1347 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1354 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1355 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1356 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1365 void SV_Physics_Pusher (prvm_edict_t *ent)
1367 float thinktime, oldltime, movetime;
1369 oldltime = ent->fields.server->ltime;
1371 thinktime = ent->fields.server->nextthink;
1372 if (thinktime < ent->fields.server->ltime + sv.frametime)
1374 movetime = thinktime - ent->fields.server->ltime;
1379 movetime = sv.frametime;
1382 // advances ent->fields.server->ltime if not blocked
1383 SV_PushMove (ent, movetime);
1385 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1387 ent->fields.server->nextthink = 0;
1388 prog->globals.server->time = sv.time;
1389 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1390 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1391 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1397 ===============================================================================
1401 ===============================================================================
1404 static float unstickoffsets[] =
1406 // poutting -/+z changes first as they are least weird
1421 typedef enum unstickresult_e
1429 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1433 // if not stuck in a bmodel, just return
1434 if (!SV_TestEntityPosition(ent, vec3_origin))
1435 return UNSTICK_GOOD;
1437 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1439 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1441 VectorCopy(unstickoffsets + i, offset);
1442 SV_LinkEdict (ent, true);
1443 return UNSTICK_UNSTUCK;
1447 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1448 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1450 for(i = 2; i <= maxunstick; ++i)
1452 VectorClear(offset);
1454 if (!SV_TestEntityPosition(ent, offset))
1456 SV_LinkEdict (ent, true);
1457 return UNSTICK_UNSTUCK;
1460 if (!SV_TestEntityPosition(ent, offset))
1462 SV_LinkEdict (ent, true);
1463 return UNSTICK_UNSTUCK;
1467 return UNSTICK_STUCK;
1470 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1473 switch(SV_UnstickEntityReturnOffset(ent, offset))
1477 case UNSTICK_UNSTUCK:
1478 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]);
1481 if (developer.integer >= 100)
1482 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1485 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1494 This is a big hack to try and fix the rare case of getting stuck in the world
1498 void SV_CheckStuck (prvm_edict_t *ent)
1502 switch(SV_UnstickEntityReturnOffset(ent, offset))
1505 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1507 case UNSTICK_UNSTUCK:
1508 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]);
1511 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1512 if (!SV_TestEntityPosition(ent, offset))
1514 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1515 SV_LinkEdict (ent, true);
1518 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1521 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1531 qboolean SV_CheckWater (prvm_edict_t *ent)
1534 int nNativeContents;
1537 point[0] = ent->fields.server->origin[0];
1538 point[1] = ent->fields.server->origin[1];
1539 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1541 // DRESK - Support for Entity Contents Transition Event
1542 // NOTE: Some logic needed to be slightly re-ordered
1543 // to not affect performance and allow for the feature.
1545 // Acquire Super Contents Prior to Resets
1546 cont = SV_PointSuperContents(point);
1547 // Acquire Native Contents Here
1548 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1550 // DRESK - Support for Entity Contents Transition Event
1551 if(ent->fields.server->watertype)
1552 // Entity did NOT Spawn; Check
1553 SV_CheckContentsTransition(ent, nNativeContents);
1556 ent->fields.server->waterlevel = 0;
1557 ent->fields.server->watertype = CONTENTS_EMPTY;
1558 cont = SV_PointSuperContents(point);
1559 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1561 ent->fields.server->watertype = nNativeContents;
1562 ent->fields.server->waterlevel = 1;
1563 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1564 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1566 ent->fields.server->waterlevel = 2;
1567 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1568 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1569 ent->fields.server->waterlevel = 3;
1573 return ent->fields.server->waterlevel > 1;
1582 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1585 vec3_t forward, into, side;
1587 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1588 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1590 // cut the tangential velocity
1591 i = DotProduct (stepnormal, ent->fields.server->velocity);
1592 VectorScale (stepnormal, i, into);
1593 VectorSubtract (ent->fields.server->velocity, into, side);
1594 ent->fields.server->velocity[0] = side[0] * (1 + d);
1595 ent->fields.server->velocity[1] = side[1] * (1 + d);
1601 =====================
1604 Player has come to a dead stop, possibly due to the problem with limited
1605 float precision at some angle joins in the BSP hull.
1607 Try fixing by pushing one pixel in each direction.
1609 This is a hack, but in the interest of good gameplay...
1610 ======================
1612 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1617 VectorCopy (ent->fields.server->origin, oldorg);
1620 for (i=0 ; i<8 ; i++)
1622 // try pushing a little in an axial direction
1625 case 0: dir[0] = 2; dir[1] = 0; break;
1626 case 1: dir[0] = 0; dir[1] = 2; break;
1627 case 2: dir[0] = -2; dir[1] = 0; break;
1628 case 3: dir[0] = 0; dir[1] = -2; break;
1629 case 4: dir[0] = 2; dir[1] = 2; break;
1630 case 5: dir[0] = -2; dir[1] = 2; break;
1631 case 6: dir[0] = 2; dir[1] = -2; break;
1632 case 7: dir[0] = -2; dir[1] = -2; break;
1635 SV_PushEntity (ent, dir, false);
1637 // retry the original move
1638 ent->fields.server->velocity[0] = oldvel[0];
1639 ent->fields.server->velocity[1] = oldvel[1];
1640 ent->fields.server->velocity[2] = 0;
1641 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1643 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1644 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1646 Con_DPrint("TryUnstick - success.\n");
1650 // go back to the original pos and try again
1651 VectorCopy (oldorg, ent->fields.server->origin);
1655 VectorClear (ent->fields.server->velocity);
1656 Con_DPrint("TryUnstick - failure.\n");
1662 =====================
1665 Only used by players
1666 ======================
1668 void SV_WalkMove (prvm_edict_t *ent)
1670 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1671 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1673 qboolean applygravity;
1675 // if frametime is 0 (due to client sending the same timestamp twice),
1677 if (sv.frametime <= 0)
1680 SV_CheckStuck (ent);
1682 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1684 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1686 SV_CheckVelocity(ent);
1688 // do a regular slide move unless it looks like you ran into a step
1689 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1691 VectorCopy (ent->fields.server->origin, start_origin);
1692 VectorCopy (ent->fields.server->velocity, start_velocity);
1694 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1696 // if the move did not hit the ground at any point, we're not on ground
1698 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1700 SV_CheckVelocity(ent);
1701 SV_LinkEdict (ent, true);
1703 VectorCopy(ent->fields.server->origin, originalmove_origin);
1704 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1705 originalmove_clip = clip;
1706 originalmove_flags = (int)ent->fields.server->flags;
1707 originalmove_groundentity = ent->fields.server->groundentity;
1709 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1712 if (sv_nostep.integer)
1715 // if move didn't block on a step, return
1718 // if move was not trying to move into the step, return
1719 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1722 if (ent->fields.server->movetype != MOVETYPE_FLY)
1724 // return if gibbed by a trigger
1725 if (ent->fields.server->movetype != MOVETYPE_WALK)
1728 // only step up while jumping if that is enabled
1729 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1730 if (!oldonground && ent->fields.server->waterlevel == 0)
1734 // try moving up and forward to go up a step
1735 // back to start pos
1736 VectorCopy (start_origin, ent->fields.server->origin);
1737 VectorCopy (start_velocity, ent->fields.server->velocity);
1740 VectorClear (upmove);
1741 upmove[2] = sv_stepheight.value;
1742 // FIXME: don't link?
1743 SV_PushEntity(ent, upmove, false);
1746 ent->fields.server->velocity[2] = 0;
1747 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1748 ent->fields.server->velocity[2] += start_velocity[2];
1750 SV_CheckVelocity(ent);
1751 SV_LinkEdict (ent, true);
1753 // check for stuckness, possibly due to the limited precision of floats
1754 // in the clipping hulls
1756 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1757 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1759 //Con_Printf("wall\n");
1760 // stepping up didn't make any progress, revert to original move
1761 VectorCopy(originalmove_origin, ent->fields.server->origin);
1762 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1763 //clip = originalmove_clip;
1764 ent->fields.server->flags = originalmove_flags;
1765 ent->fields.server->groundentity = originalmove_groundentity;
1766 // now try to unstick if needed
1767 //clip = SV_TryUnstick (ent, oldvel);
1771 //Con_Printf("step - ");
1773 // extra friction based on view angle
1774 if (clip & 2 && sv_wallfriction.integer)
1775 SV_WallFriction (ent, stepnormal);
1777 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1778 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))
1782 VectorClear (downmove);
1783 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1784 // FIXME: don't link?
1785 downtrace = SV_PushEntity (ent, downmove, false);
1787 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1789 // this has been disabled so that you can't jump when you are stepping
1790 // up while already jumping (also known as the Quake2 double jump bug)
1792 // LordHavoc: disabled this check so you can walk on monsters/players
1793 //if (ent->fields.server->solid == SOLID_BSP)
1795 //Con_Printf("onground\n");
1796 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1797 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1803 //Con_Printf("slope\n");
1804 // if the push down didn't end up on good ground, use the move without
1805 // the step up. This happens near wall / slope combinations, and can
1806 // cause the player to hop up higher on a slope too steep to climb
1807 VectorCopy(originalmove_origin, ent->fields.server->origin);
1808 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1809 //clip = originalmove_clip;
1810 ent->fields.server->flags = originalmove_flags;
1811 ent->fields.server->groundentity = originalmove_groundentity;
1814 SV_CheckVelocity(ent);
1815 SV_LinkEdict (ent, true);
1818 //============================================================================
1824 Entities that are "stuck" to another entity
1827 void SV_Physics_Follow (prvm_edict_t *ent)
1829 vec3_t vf, vr, vu, angles, v;
1833 if (!SV_RunThink (ent))
1836 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1837 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1838 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])
1840 // quick case for no rotation
1841 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1845 angles[0] = -ent->fields.server->punchangle[0];
1846 angles[1] = ent->fields.server->punchangle[1];
1847 angles[2] = ent->fields.server->punchangle[2];
1848 AngleVectors (angles, vf, vr, vu);
1849 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];
1850 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];
1851 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];
1852 angles[0] = -e->fields.server->angles[0];
1853 angles[1] = e->fields.server->angles[1];
1854 angles[2] = e->fields.server->angles[2];
1855 AngleVectors (angles, vf, vr, vu);
1856 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1857 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1858 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1860 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1861 SV_LinkEdict (ent, true);
1865 ==============================================================================
1869 ==============================================================================
1874 SV_CheckWaterTransition
1878 void SV_CheckWaterTransition (prvm_edict_t *ent)
1881 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1882 if (!ent->fields.server->watertype)
1884 // just spawned here
1885 ent->fields.server->watertype = cont;
1886 ent->fields.server->waterlevel = 1;
1890 // DRESK - Support for Entity Contents Transition Event
1891 // NOTE: Call here BEFORE updating the watertype below,
1892 // and suppress watersplash sound if a valid function
1893 // call was made to allow for custom "splash" sounds.
1894 if( !SV_CheckContentsTransition(ent, cont) )
1895 { // Contents Transition Function Invalid; Potentially Play Water Sound
1896 // check if the entity crossed into or out of water
1897 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1898 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1901 if (cont <= CONTENTS_WATER)
1903 ent->fields.server->watertype = cont;
1904 ent->fields.server->waterlevel = 1;
1908 ent->fields.server->watertype = CONTENTS_EMPTY;
1909 ent->fields.server->waterlevel = 0;
1917 Toss, bounce, and fly movement. When onground, do nothing.
1920 void SV_Physics_Toss (prvm_edict_t *ent)
1927 // if onground, return without moving
1928 if ((int)ent->fields.server->flags & FL_ONGROUND)
1930 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1932 // don't stick to ground if onground and moving upward
1933 ent->fields.server->flags -= FL_ONGROUND;
1935 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1937 // we can trust FL_ONGROUND if groundentity is world because it never moves
1940 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1942 // if ent was supported by a brush model on previous frame,
1943 // and groundentity is now freed, set groundentity to 0 (world)
1944 // which leaves it suspended in the air
1945 ent->fields.server->groundentity = 0;
1949 ent->priv.server->suspendedinairflag = false;
1951 SV_CheckVelocity (ent);
1954 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1955 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1958 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1960 movetime = sv.frametime;
1961 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
1964 VectorScale (ent->fields.server->velocity, movetime, move);
1965 trace = SV_PushEntity (ent, move, true);
1966 if (ent->priv.server->free)
1968 if (trace.bmodelstartsolid)
1970 // try to unstick the entity
1971 SV_UnstickEntity(ent);
1972 trace = SV_PushEntity (ent, move, false);
1973 if (ent->priv.server->free)
1976 if (trace.fraction == 1)
1978 movetime *= 1 - min(1, trace.fraction);
1979 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1981 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1982 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1984 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1986 float d, ent_gravity;
1988 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1989 // LordHavoc: fixed grenades not bouncing when fired down a slope
1990 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1991 if (val!=0 && val->_float)
1992 ent_gravity = val->_float;
1995 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1997 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1998 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * (60.0 / 800.0) * ent_gravity)
2000 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2001 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2002 VectorClear (ent->fields.server->velocity);
2003 VectorClear (ent->fields.server->avelocity);
2006 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2010 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * (60.0 / 800.0) * ent_gravity)
2012 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2013 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2014 VectorClear (ent->fields.server->velocity);
2015 VectorClear (ent->fields.server->avelocity);
2018 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2023 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2024 if (trace.plane.normal[2] > 0.7)
2026 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2027 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2028 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2029 ent->priv.server->suspendedinairflag = true;
2030 VectorClear (ent->fields.server->velocity);
2031 VectorClear (ent->fields.server->avelocity);
2034 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2036 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2040 // check for in water
2041 SV_CheckWaterTransition (ent);
2045 ===============================================================================
2049 ===============================================================================
2056 Monsters freefall when they don't have a ground entity, otherwise
2057 all movement is done with discrete steps.
2059 This is also used for objects that have become still on the ground, but
2060 will fall if the floor is pulled out from under them.
2063 void SV_Physics_Step (prvm_edict_t *ent)
2065 int flags = (int)ent->fields.server->flags;
2068 // Backup Velocity in the event that movetypesteplandevent is called,
2069 // to provide a parameter with the entity's velocity at impact.
2070 prvm_eval_t *movetypesteplandevent;
2071 vec3_t backupVelocity;
2072 VectorCopy(ent->fields.server->velocity, backupVelocity);
2073 // don't fall at all if fly/swim
2074 if (!(flags & (FL_FLY | FL_SWIM)))
2076 if (flags & FL_ONGROUND)
2078 // freefall if onground and moving upward
2079 // freefall if not standing on a world surface (it may be a lift or trap door)
2080 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2082 ent->fields.server->flags -= FL_ONGROUND;
2083 SV_CheckVelocity(ent);
2084 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2085 SV_LinkEdict(ent, true);
2086 ent->priv.server->waterposition_forceupdate = true;
2091 // freefall if not onground
2092 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2094 SV_CheckVelocity(ent);
2095 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2096 SV_LinkEdict(ent, true);
2099 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2101 // DRESK - Check for Entity Land Event Function
2102 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2104 if(movetypesteplandevent->function)
2105 { // Valid Function; Execute
2106 // Prepare Parameters
2107 // Assign Velocity at Impact
2108 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2109 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2110 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2112 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2113 // Execute VM Function
2114 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2117 // Check for Engine Landing Sound
2118 if(sv_sound_land.string)
2119 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2121 ent->priv.server->waterposition_forceupdate = true;
2126 if (!SV_RunThink(ent))
2129 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2131 ent->priv.server->waterposition_forceupdate = false;
2132 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2133 SV_CheckWaterTransition(ent);
2137 //============================================================================
2139 static void SV_Physics_Entity (prvm_edict_t *ent)
2141 // don't run think/move on newly spawned projectiles as it messes up
2142 // movement interpolation and rocket trails, and is inconsistent with
2143 // respect to entities spawned in the same frame
2144 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2145 // but if it spawns a lower numbered ent, it doesn't - this never moves
2146 // ents in the first frame regardless)
2147 qboolean runmove = ent->priv.server->move;
2148 ent->priv.server->move = true;
2149 if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2151 switch ((int) ent->fields.server->movetype)
2154 case MOVETYPE_FAKEPUSH:
2155 SV_Physics_Pusher (ent);
2158 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2159 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2162 case MOVETYPE_FOLLOW:
2163 SV_Physics_Follow (ent);
2165 case MOVETYPE_NOCLIP:
2166 if (SV_RunThink(ent))
2169 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2170 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2172 SV_LinkEdict(ent, false);
2175 SV_Physics_Step (ent);
2178 if (SV_RunThink (ent))
2182 case MOVETYPE_BOUNCE:
2183 case MOVETYPE_BOUNCEMISSILE:
2184 case MOVETYPE_FLYMISSILE:
2187 if (SV_RunThink (ent))
2188 SV_Physics_Toss (ent);
2191 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2196 void SV_Physics_ClientMove(void)
2199 ent = host_client->edict;
2201 // call player physics, this needs the proper frametime
2202 prog->globals.server->frametime = sv.frametime;
2205 // call standard client pre-think, with frametime = 0
2206 prog->globals.server->time = sv.time;
2207 prog->globals.server->frametime = 0;
2208 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2209 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2210 prog->globals.server->frametime = sv.frametime;
2212 // make sure the velocity is sane (not a NaN)
2213 SV_CheckVelocity(ent);
2214 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2215 // player_run/player_stand1 does not horribly malfunction if the
2216 // velocity becomes a number that is both == 0 and != 0
2217 // (sounds to me like NaN but to be absolutely safe...)
2218 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2219 VectorClear(ent->fields.server->velocity);
2221 // perform MOVETYPE_WALK behavior
2224 // call standard player post-think, with frametime = 0
2225 prog->globals.server->time = sv.time;
2226 prog->globals.server->frametime = 0;
2227 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2228 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2229 prog->globals.server->frametime = sv.frametime;
2231 if(ent->fields.server->fixangle)
2233 // angle fixing was requested by physics code...
2234 // so store the current angles for later use
2235 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2236 host_client->fixangle_angles_set = TRUE;
2238 // and clear fixangle for the next frame
2239 ent->fields.server->fixangle = 0;
2243 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2245 // don't do physics on disconnected clients, FrikBot relies on this
2246 if (!host_client->spawned)
2248 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2252 // don't run physics here if running asynchronously
2253 if (host_client->clmovement_inputtimeout <= 0)
2256 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2259 // make sure the velocity is sane (not a NaN)
2260 SV_CheckVelocity(ent);
2261 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2262 // player_run/player_stand1 does not horribly malfunction if the
2263 // velocity becomes a number that is both == 0 and != 0
2264 // (sounds to me like NaN but to be absolutely safe...)
2265 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2266 VectorClear(ent->fields.server->velocity);
2268 // call standard client pre-think
2269 prog->globals.server->time = sv.time;
2270 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2271 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2272 SV_CheckVelocity (ent);
2274 switch ((int) ent->fields.server->movetype)
2277 case MOVETYPE_FAKEPUSH:
2278 SV_Physics_Pusher (ent);
2281 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2282 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2285 case MOVETYPE_FOLLOW:
2286 SV_Physics_Follow (ent);
2288 case MOVETYPE_NOCLIP:
2291 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2292 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2295 SV_Physics_Step (ent);
2299 // don't run physics here if running asynchronously
2300 if (host_client->clmovement_inputtimeout <= 0)
2304 case MOVETYPE_BOUNCE:
2305 case MOVETYPE_BOUNCEMISSILE:
2306 case MOVETYPE_FLYMISSILE:
2309 SV_Physics_Toss (ent);
2316 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2320 // decrement the countdown variable used to decide when to go back to
2321 // synchronous physics
2322 if (host_client->clmovement_inputtimeout > sv.frametime)
2323 host_client->clmovement_inputtimeout -= sv.frametime;
2325 host_client->clmovement_inputtimeout = 0;
2327 SV_CheckVelocity (ent);
2329 SV_LinkEdict (ent, true);
2331 SV_CheckVelocity (ent);
2333 // call standard player post-think
2334 prog->globals.server->time = sv.time;
2335 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2336 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2338 if(ent->fields.server->fixangle)
2340 // angle fixing was requested by physics code...
2341 // so store the current angles for later use
2342 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2343 host_client->fixangle_angles_set = TRUE;
2345 // and clear fixangle for the next frame
2346 ent->fields.server->fixangle = 0;
2356 void SV_Physics (void)
2361 // let the progs know that a new frame has started
2362 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2363 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2364 prog->globals.server->time = sv.time;
2365 prog->globals.server->frametime = sv.frametime;
2366 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2369 // treat each object in turn
2372 // if force_retouch, relink all the entities
2373 if (prog->globals.server->force_retouch > 0)
2374 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2375 if (!ent->priv.server->free)
2376 SV_LinkEdict (ent, true); // force retouch even for stationary
2378 // run physics on the client entities
2379 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2380 if (!ent->priv.server->free)
2381 SV_Physics_ClientEntity(ent);
2383 // run physics on all the non-client entities
2384 if (!sv_freezenonclients.integer)
2385 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2386 if (!ent->priv.server->free)
2387 SV_Physics_Entity(ent);
2389 if (prog->globals.server->force_retouch > 0)
2390 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2392 // LordHavoc: endframe support
2393 if (prog->funcoffsets.EndFrame)
2395 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2396 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2397 prog->globals.server->time = sv.time;
2398 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2401 // decrement prog->num_edicts if the highest number entities died
2402 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2404 if (!sv_freezenonclients.integer)
2405 sv.time += sv.frametime;