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 8 = teleported by touch method
770 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
773 static float SV_Gravity (prvm_edict_t *ent);
774 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
775 // LordHavoc: increased from 5 to 32
776 #define MAX_CLIP_PLANES 32
777 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
779 int blocked, bumpcount;
781 float d, time_left, gravity;
782 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
792 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
794 gravity = SV_Gravity(ent) * 0.5f;
795 ent->fields.server->velocity[2] -= gravity;
799 applygravity = false;
800 ent->fields.server->velocity[2] -= SV_Gravity(ent);
804 VectorCopy(ent->fields.server->velocity, original_velocity);
805 VectorCopy(ent->fields.server->velocity, primal_velocity);
808 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
810 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
813 VectorScale(ent->fields.server->velocity, time_left, push);
815 VectorAdd(ent->fields.server->origin, push, end);
817 trace = SV_PushEntity(ent, push, false, false); // the caller calls SV_LinkEntity on the own later
819 VectorCopy(trace.endpos, push); // convert to float for comparing
820 if(!VectorCompare(push, ent->fields.server->origin))
822 // we got teleported by a touch function
823 // let's abort the move
829 //if (trace.fraction < 0.002)
834 VectorCopy(ent->fields.server->origin, start);
835 start[2] += 3;//0.03125;
836 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
837 end[2] += 3;//0.03125;
838 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
839 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)))
841 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
847 for (i = 0;i < numplanes;i++)
849 VectorCopy(ent->fields.server->origin, start);
850 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
851 VectorMA(start, 3, planes[i], start);
852 VectorMA(end, 3, planes[i], end);
853 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
854 if (trace.fraction < testtrace.fraction)
857 VectorCopy(start, ent->fields.server->origin);
862 // VectorAdd(ent->fields.server->origin, planes[j], start);
868 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);
869 if (trace.fraction < 1)
870 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
875 if (trace.bmodelstartsolid)
877 // LordHavoc: note: this code is what makes entities stick in place
878 // if embedded in world only (you can walk through other objects if
880 // entity is trapped in another solid
881 VectorClear(ent->fields.server->velocity);
886 if (trace.fraction == 1)
888 if (trace.plane.normal[2])
890 if (trace.plane.normal[2] > 0.7)
897 Con_Printf ("SV_FlyMove: !trace.ent");
898 trace.ent = prog->edicts;
901 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
902 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
909 // save the trace for player extrafriction
911 VectorCopy(trace.plane.normal, stepnormal);
913 if (trace.fraction >= 0.001)
915 // actually covered some distance
916 VectorCopy(ent->fields.server->velocity, original_velocity);
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) && !(blocked & 8))
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, qboolean dolink)
1058 VectorAdd (ent->fields.server->origin, push, end);
1060 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1061 type = MOVE_MISSILE;
1062 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1063 type = MOVE_NOMONSTERS; // only clip against bmodels
1067 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1068 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1071 VectorCopy (trace.endpos, ent->fields.server->origin);
1073 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)));
1077 SV_LinkEdict (ent, dolink);
1078 SV_Impact (ent, &trace);
1081 SV_LinkEdict (ent, true);
1093 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1096 int pusherowner, pusherprog;
1099 float savesolid, movetime2, pushltime;
1100 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1102 int numcheckentities;
1103 static prvm_edict_t *checkentities[MAX_EDICTS];
1104 dp_model_t *pushermodel;
1106 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1107 unsigned short moved_edicts[MAX_EDICTS];
1109 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])
1111 pusher->fields.server->ltime += movetime;
1115 switch ((int) pusher->fields.server->solid)
1117 // LordHavoc: valid pusher types
1120 case SOLID_SLIDEBOX:
1121 case SOLID_CORPSE: // LordHavoc: this would be weird...
1123 // LordHavoc: no collisions
1126 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1127 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1128 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1129 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1130 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1131 pusher->fields.server->ltime += movetime;
1132 SV_LinkEdict (pusher, false);
1135 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1138 index = (int) pusher->fields.server->modelindex;
1139 if (index < 1 || index >= MAX_MODELS)
1141 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1144 pushermodel = sv.models[index];
1145 pusherowner = pusher->fields.server->owner;
1146 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1148 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1150 movetime2 = movetime;
1151 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1152 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1153 if (moveangle[0] || moveangle[2])
1155 for (i = 0;i < 3;i++)
1159 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1160 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1164 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1165 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1169 else if (moveangle[1])
1171 for (i = 0;i < 3;i++)
1175 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1176 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1180 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1181 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1187 for (i = 0;i < 3;i++)
1191 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1192 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1196 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1197 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1202 VectorNegate (moveangle, a);
1203 AngleVectorsFLU (a, forward, left, up);
1205 VectorCopy (pusher->fields.server->origin, pushorig);
1206 VectorCopy (pusher->fields.server->angles, pushang);
1207 pushltime = pusher->fields.server->ltime;
1209 // move the pusher to its final position
1211 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1212 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1213 pusher->fields.server->ltime += movetime;
1214 SV_LinkEdict (pusher, false);
1217 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1218 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1219 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);
1220 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1222 savesolid = pusher->fields.server->solid;
1224 // see if any solid entities are inside the final position
1227 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1228 for (e = 0;e < numcheckentities;e++)
1230 prvm_edict_t *check = checkentities[e];
1231 if (check->fields.server->movetype == MOVETYPE_NONE
1232 || check->fields.server->movetype == MOVETYPE_PUSH
1233 || check->fields.server->movetype == MOVETYPE_FOLLOW
1234 || check->fields.server->movetype == MOVETYPE_NOCLIP
1235 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1238 if (check->fields.server->owner == pusherprog)
1241 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1244 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1246 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1247 check->priv.server->waterposition_forceupdate = true;
1249 checkcontents = SV_GenericHitSuperContentsMask(check);
1251 // if the entity is standing on the pusher, it will definitely be moved
1252 // if the entity is not standing on the pusher, but is in the pusher's
1253 // final position, move it
1254 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1256 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);
1257 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1258 if (!trace.startsolid)
1260 //Con_Printf("- not in solid\n");
1268 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1269 org2[0] = DotProduct (org, forward);
1270 org2[1] = DotProduct (org, left);
1271 org2[2] = DotProduct (org, up);
1272 VectorSubtract (org2, org, move);
1273 VectorAdd (move, move1, move);
1276 VectorCopy (move1, move);
1278 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1280 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1281 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1282 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1284 // try moving the contacted entity
1285 pusher->fields.server->solid = SOLID_NOT;
1286 trace = SV_PushEntity (check, move, true, true);
1287 // FIXME: turn players specially
1288 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1289 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1290 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1292 // this trace.fraction < 1 check causes items to fall off of pushers
1293 // if they pass under or through a wall
1294 // the groundentity check causes items to fall off of ledges
1295 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1296 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1298 // if it is still inside the pusher, block
1299 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);
1300 if (trace.startsolid)
1302 // try moving the contacted entity a tiny bit further to account for precision errors
1304 pusher->fields.server->solid = SOLID_NOT;
1305 VectorScale(move, 1.1, move2);
1306 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1307 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1308 SV_PushEntity (check, move2, true, true);
1309 pusher->fields.server->solid = savesolid;
1310 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);
1311 if (trace.startsolid)
1313 // try moving the contacted entity a tiny bit less to account for precision errors
1314 pusher->fields.server->solid = SOLID_NOT;
1315 VectorScale(move, 0.9, move2);
1316 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1317 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1318 SV_PushEntity (check, move2, true, true);
1319 pusher->fields.server->solid = savesolid;
1320 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);
1321 if (trace.startsolid)
1323 // still inside pusher, so it's really blocked
1326 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1328 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1331 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1332 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1336 VectorCopy (pushorig, pusher->fields.server->origin);
1337 VectorCopy (pushang, pusher->fields.server->angles);
1338 pusher->fields.server->ltime = pushltime;
1339 SV_LinkEdict (pusher, false);
1341 // move back any entities we already moved
1342 for (i = 0;i < num_moved;i++)
1344 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1345 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1346 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1347 SV_LinkEdict (ed, false);
1350 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1351 if (pusher->fields.server->blocked)
1353 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1354 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1355 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1362 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1363 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1364 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1373 void SV_Physics_Pusher (prvm_edict_t *ent)
1375 float thinktime, oldltime, movetime;
1377 oldltime = ent->fields.server->ltime;
1379 thinktime = ent->fields.server->nextthink;
1380 if (thinktime < ent->fields.server->ltime + sv.frametime)
1382 movetime = thinktime - ent->fields.server->ltime;
1387 movetime = sv.frametime;
1390 // advances ent->fields.server->ltime if not blocked
1391 SV_PushMove (ent, movetime);
1393 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1395 ent->fields.server->nextthink = 0;
1396 prog->globals.server->time = sv.time;
1397 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1398 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1399 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1405 ===============================================================================
1409 ===============================================================================
1412 static float unstickoffsets[] =
1414 // poutting -/+z changes first as they are least weird
1429 typedef enum unstickresult_e
1437 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1441 // if not stuck in a bmodel, just return
1442 if (!SV_TestEntityPosition(ent, vec3_origin))
1443 return UNSTICK_GOOD;
1445 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1447 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1449 VectorCopy(unstickoffsets + i, offset);
1450 SV_LinkEdict (ent, true);
1451 return UNSTICK_UNSTUCK;
1455 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1456 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1458 for(i = 2; i <= maxunstick; ++i)
1460 VectorClear(offset);
1462 if (!SV_TestEntityPosition(ent, offset))
1464 SV_LinkEdict (ent, true);
1465 return UNSTICK_UNSTUCK;
1468 if (!SV_TestEntityPosition(ent, offset))
1470 SV_LinkEdict (ent, true);
1471 return UNSTICK_UNSTUCK;
1475 return UNSTICK_STUCK;
1478 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1481 switch(SV_UnstickEntityReturnOffset(ent, offset))
1485 case UNSTICK_UNSTUCK:
1486 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]);
1489 if (developer.integer >= 100)
1490 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1493 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1502 This is a big hack to try and fix the rare case of getting stuck in the world
1506 void SV_CheckStuck (prvm_edict_t *ent)
1510 switch(SV_UnstickEntityReturnOffset(ent, offset))
1513 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1515 case UNSTICK_UNSTUCK:
1516 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]);
1519 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1520 if (!SV_TestEntityPosition(ent, offset))
1522 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1523 SV_LinkEdict (ent, true);
1526 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1529 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1539 qboolean SV_CheckWater (prvm_edict_t *ent)
1542 int nNativeContents;
1545 point[0] = ent->fields.server->origin[0];
1546 point[1] = ent->fields.server->origin[1];
1547 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1549 // DRESK - Support for Entity Contents Transition Event
1550 // NOTE: Some logic needed to be slightly re-ordered
1551 // to not affect performance and allow for the feature.
1553 // Acquire Super Contents Prior to Resets
1554 cont = SV_PointSuperContents(point);
1555 // Acquire Native Contents Here
1556 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1558 // DRESK - Support for Entity Contents Transition Event
1559 if(ent->fields.server->watertype)
1560 // Entity did NOT Spawn; Check
1561 SV_CheckContentsTransition(ent, nNativeContents);
1564 ent->fields.server->waterlevel = 0;
1565 ent->fields.server->watertype = CONTENTS_EMPTY;
1566 cont = SV_PointSuperContents(point);
1567 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1569 ent->fields.server->watertype = nNativeContents;
1570 ent->fields.server->waterlevel = 1;
1571 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1572 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1574 ent->fields.server->waterlevel = 2;
1575 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1576 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1577 ent->fields.server->waterlevel = 3;
1581 return ent->fields.server->waterlevel > 1;
1590 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1593 vec3_t forward, into, side;
1595 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1596 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1598 // cut the tangential velocity
1599 i = DotProduct (stepnormal, ent->fields.server->velocity);
1600 VectorScale (stepnormal, i, into);
1601 VectorSubtract (ent->fields.server->velocity, into, side);
1602 ent->fields.server->velocity[0] = side[0] * (1 + d);
1603 ent->fields.server->velocity[1] = side[1] * (1 + d);
1609 =====================
1612 Player has come to a dead stop, possibly due to the problem with limited
1613 float precision at some angle joins in the BSP hull.
1615 Try fixing by pushing one pixel in each direction.
1617 This is a hack, but in the interest of good gameplay...
1618 ======================
1620 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1625 VectorCopy (ent->fields.server->origin, oldorg);
1628 for (i=0 ; i<8 ; i++)
1630 // try pushing a little in an axial direction
1633 case 0: dir[0] = 2; dir[1] = 0; break;
1634 case 1: dir[0] = 0; dir[1] = 2; break;
1635 case 2: dir[0] = -2; dir[1] = 0; break;
1636 case 3: dir[0] = 0; dir[1] = -2; break;
1637 case 4: dir[0] = 2; dir[1] = 2; break;
1638 case 5: dir[0] = -2; dir[1] = 2; break;
1639 case 6: dir[0] = 2; dir[1] = -2; break;
1640 case 7: dir[0] = -2; dir[1] = -2; break;
1643 SV_PushEntity (ent, dir, false, true);
1645 // retry the original move
1646 ent->fields.server->velocity[0] = oldvel[0];
1647 ent->fields.server->velocity[1] = oldvel[1];
1648 ent->fields.server->velocity[2] = 0;
1649 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1651 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1652 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1654 Con_DPrint("TryUnstick - success.\n");
1658 // go back to the original pos and try again
1659 VectorCopy (oldorg, ent->fields.server->origin);
1663 VectorClear (ent->fields.server->velocity);
1664 Con_DPrint("TryUnstick - failure.\n");
1670 =====================
1673 Only used by players
1674 ======================
1676 void SV_WalkMove (prvm_edict_t *ent)
1678 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1679 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1681 qboolean applygravity;
1683 // if frametime is 0 (due to client sending the same timestamp twice),
1685 if (sv.frametime <= 0)
1688 SV_CheckStuck (ent);
1690 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1692 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1694 SV_CheckVelocity(ent);
1696 // do a regular slide move unless it looks like you ran into a step
1697 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1699 VectorCopy (ent->fields.server->origin, start_origin);
1700 VectorCopy (ent->fields.server->velocity, start_velocity);
1702 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1704 // if the move did not hit the ground at any point, we're not on ground
1706 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1708 SV_CheckVelocity(ent);
1709 SV_LinkEdict (ent, true);
1711 if(clip & 8) // teleport
1714 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1717 if (sv_nostep.integer)
1720 VectorCopy(ent->fields.server->origin, originalmove_origin);
1721 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1722 originalmove_clip = clip;
1723 originalmove_flags = (int)ent->fields.server->flags;
1724 originalmove_groundentity = ent->fields.server->groundentity;
1726 // if move didn't block on a step, return
1729 // if move was not trying to move into the step, return
1730 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1733 if (ent->fields.server->movetype != MOVETYPE_FLY)
1735 // return if gibbed by a trigger
1736 if (ent->fields.server->movetype != MOVETYPE_WALK)
1739 // only step up while jumping if that is enabled
1740 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1741 if (!oldonground && ent->fields.server->waterlevel == 0)
1745 // try moving up and forward to go up a step
1746 // back to start pos
1747 VectorCopy (start_origin, ent->fields.server->origin);
1748 VectorCopy (start_velocity, ent->fields.server->velocity);
1751 VectorClear (upmove);
1752 upmove[2] = sv_stepheight.value;
1753 SV_PushEntity(ent, upmove, false, true);
1756 ent->fields.server->velocity[2] = 0;
1757 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1758 ent->fields.server->velocity[2] += start_velocity[2];
1760 SV_CheckVelocity(ent);
1761 SV_LinkEdict (ent, true);
1763 // check for stuckness, possibly due to the limited precision of floats
1764 // in the clipping hulls
1766 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1767 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1769 //Con_Printf("wall\n");
1770 // stepping up didn't make any progress, revert to original move
1771 VectorCopy(originalmove_origin, ent->fields.server->origin);
1772 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1773 //clip = originalmove_clip;
1774 ent->fields.server->flags = originalmove_flags;
1775 ent->fields.server->groundentity = originalmove_groundentity;
1776 // now try to unstick if needed
1777 //clip = SV_TryUnstick (ent, oldvel);
1781 //Con_Printf("step - ");
1783 // extra friction based on view angle
1784 if (clip & 2 && sv_wallfriction.integer)
1785 SV_WallFriction (ent, stepnormal);
1787 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1788 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))
1792 VectorClear (downmove);
1793 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1794 downtrace = SV_PushEntity (ent, downmove, false, true);
1796 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1798 // this has been disabled so that you can't jump when you are stepping
1799 // up while already jumping (also known as the Quake2 double jump bug)
1801 // LordHavoc: disabled this check so you can walk on monsters/players
1802 //if (ent->fields.server->solid == SOLID_BSP)
1804 //Con_Printf("onground\n");
1805 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1806 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1812 //Con_Printf("slope\n");
1813 // if the push down didn't end up on good ground, use the move without
1814 // the step up. This happens near wall / slope combinations, and can
1815 // cause the player to hop up higher on a slope too steep to climb
1816 VectorCopy(originalmove_origin, ent->fields.server->origin);
1817 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1818 //clip = originalmove_clip;
1819 ent->fields.server->flags = originalmove_flags;
1820 ent->fields.server->groundentity = originalmove_groundentity;
1823 SV_CheckVelocity(ent);
1824 SV_LinkEdict (ent, true);
1827 //============================================================================
1833 Entities that are "stuck" to another entity
1836 void SV_Physics_Follow (prvm_edict_t *ent)
1838 vec3_t vf, vr, vu, angles, v;
1842 if (!SV_RunThink (ent))
1845 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1846 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1847 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])
1849 // quick case for no rotation
1850 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1854 angles[0] = -ent->fields.server->punchangle[0];
1855 angles[1] = ent->fields.server->punchangle[1];
1856 angles[2] = ent->fields.server->punchangle[2];
1857 AngleVectors (angles, vf, vr, vu);
1858 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];
1859 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];
1860 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];
1861 angles[0] = -e->fields.server->angles[0];
1862 angles[1] = e->fields.server->angles[1];
1863 angles[2] = e->fields.server->angles[2];
1864 AngleVectors (angles, vf, vr, vu);
1865 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1866 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1867 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1869 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1870 SV_LinkEdict (ent, true);
1874 ==============================================================================
1878 ==============================================================================
1883 SV_CheckWaterTransition
1887 void SV_CheckWaterTransition (prvm_edict_t *ent)
1890 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1891 if (!ent->fields.server->watertype)
1893 // just spawned here
1894 ent->fields.server->watertype = cont;
1895 ent->fields.server->waterlevel = 1;
1899 // DRESK - Support for Entity Contents Transition Event
1900 // NOTE: Call here BEFORE updating the watertype below,
1901 // and suppress watersplash sound if a valid function
1902 // call was made to allow for custom "splash" sounds.
1903 if( !SV_CheckContentsTransition(ent, cont) )
1904 { // Contents Transition Function Invalid; Potentially Play Water Sound
1905 // check if the entity crossed into or out of water
1906 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1907 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1910 if (cont <= CONTENTS_WATER)
1912 ent->fields.server->watertype = cont;
1913 ent->fields.server->waterlevel = 1;
1917 ent->fields.server->watertype = CONTENTS_EMPTY;
1918 ent->fields.server->waterlevel = 0;
1926 Toss, bounce, and fly movement. When onground, do nothing.
1929 void SV_Physics_Toss (prvm_edict_t *ent)
1936 // if onground, return without moving
1937 if ((int)ent->fields.server->flags & FL_ONGROUND)
1939 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1941 // don't stick to ground if onground and moving upward
1942 ent->fields.server->flags -= FL_ONGROUND;
1944 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1946 // we can trust FL_ONGROUND if groundentity is world because it never moves
1949 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1951 // if ent was supported by a brush model on previous frame,
1952 // and groundentity is now freed, set groundentity to 0 (world)
1953 // which leaves it suspended in the air
1954 ent->fields.server->groundentity = 0;
1958 ent->priv.server->suspendedinairflag = false;
1960 SV_CheckVelocity (ent);
1963 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1964 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1967 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1969 movetime = sv.frametime;
1970 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
1973 VectorScale (ent->fields.server->velocity, movetime, move);
1974 trace = SV_PushEntity (ent, move, true, true);
1975 if (ent->priv.server->free)
1977 if (trace.bmodelstartsolid)
1979 // try to unstick the entity
1980 SV_UnstickEntity(ent);
1981 trace = SV_PushEntity (ent, move, false, true);
1982 if (ent->priv.server->free)
1985 if (trace.fraction == 1)
1987 movetime *= 1 - min(1, trace.fraction);
1988 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1990 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1991 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1993 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1995 float d, ent_gravity;
1997 float bouncefactor = 0.5f;
1998 float bouncestop = 60.0f / 800.0f;
2000 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2001 if (val!=0 && val->_float)
2002 bouncefactor = val->_float;
2004 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2005 if (val!=0 && val->_float)
2006 bouncestop = val->_float;
2008 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2009 // LordHavoc: fixed grenades not bouncing when fired down a slope
2010 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2011 if (val!=0 && val->_float)
2012 ent_gravity = val->_float;
2015 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2017 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2018 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2020 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2021 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2022 VectorClear (ent->fields.server->velocity);
2023 VectorClear (ent->fields.server->avelocity);
2026 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2030 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2032 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2033 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2034 VectorClear (ent->fields.server->velocity);
2035 VectorClear (ent->fields.server->avelocity);
2038 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2043 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2044 if (trace.plane.normal[2] > 0.7)
2046 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2047 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2048 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2049 ent->priv.server->suspendedinairflag = true;
2050 VectorClear (ent->fields.server->velocity);
2051 VectorClear (ent->fields.server->avelocity);
2054 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2056 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2060 // check for in water
2061 SV_CheckWaterTransition (ent);
2065 ===============================================================================
2069 ===============================================================================
2076 Monsters freefall when they don't have a ground entity, otherwise
2077 all movement is done with discrete steps.
2079 This is also used for objects that have become still on the ground, but
2080 will fall if the floor is pulled out from under them.
2083 void SV_Physics_Step (prvm_edict_t *ent)
2085 int flags = (int)ent->fields.server->flags;
2088 // Backup Velocity in the event that movetypesteplandevent is called,
2089 // to provide a parameter with the entity's velocity at impact.
2090 prvm_eval_t *movetypesteplandevent;
2091 vec3_t backupVelocity;
2092 VectorCopy(ent->fields.server->velocity, backupVelocity);
2093 // don't fall at all if fly/swim
2094 if (!(flags & (FL_FLY | FL_SWIM)))
2096 if (flags & FL_ONGROUND)
2098 // freefall if onground and moving upward
2099 // freefall if not standing on a world surface (it may be a lift or trap door)
2100 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2102 ent->fields.server->flags -= FL_ONGROUND;
2103 SV_CheckVelocity(ent);
2104 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2105 SV_LinkEdict(ent, true);
2106 ent->priv.server->waterposition_forceupdate = true;
2111 // freefall if not onground
2112 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2114 SV_CheckVelocity(ent);
2115 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2116 SV_LinkEdict(ent, true);
2119 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2121 // DRESK - Check for Entity Land Event Function
2122 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2124 if(movetypesteplandevent->function)
2125 { // Valid Function; Execute
2126 // Prepare Parameters
2127 // Assign Velocity at Impact
2128 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2129 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2130 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2132 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2133 // Execute VM Function
2134 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2137 // Check for Engine Landing Sound
2138 if(sv_sound_land.string)
2139 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2141 ent->priv.server->waterposition_forceupdate = true;
2146 if (!SV_RunThink(ent))
2149 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2151 ent->priv.server->waterposition_forceupdate = false;
2152 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2153 SV_CheckWaterTransition(ent);
2157 //============================================================================
2159 static void SV_Physics_Entity (prvm_edict_t *ent)
2161 // don't run think/move on newly spawned projectiles as it messes up
2162 // movement interpolation and rocket trails, and is inconsistent with
2163 // respect to entities spawned in the same frame
2164 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2165 // but if it spawns a lower numbered ent, it doesn't - this never moves
2166 // ents in the first frame regardless)
2167 qboolean runmove = ent->priv.server->move;
2168 ent->priv.server->move = true;
2169 if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2171 switch ((int) ent->fields.server->movetype)
2174 case MOVETYPE_FAKEPUSH:
2175 SV_Physics_Pusher (ent);
2178 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2179 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2182 case MOVETYPE_FOLLOW:
2183 SV_Physics_Follow (ent);
2185 case MOVETYPE_NOCLIP:
2186 if (SV_RunThink(ent))
2189 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2190 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2192 SV_LinkEdict(ent, false);
2195 SV_Physics_Step (ent);
2198 if (SV_RunThink (ent))
2202 case MOVETYPE_BOUNCE:
2203 case MOVETYPE_BOUNCEMISSILE:
2204 case MOVETYPE_FLYMISSILE:
2207 if (SV_RunThink (ent))
2208 SV_Physics_Toss (ent);
2211 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2216 void SV_Physics_ClientMove(void)
2219 ent = host_client->edict;
2221 // call player physics, this needs the proper frametime
2222 prog->globals.server->frametime = sv.frametime;
2225 // call standard client pre-think, with frametime = 0
2226 prog->globals.server->time = sv.time;
2227 prog->globals.server->frametime = 0;
2228 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2229 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2230 prog->globals.server->frametime = sv.frametime;
2232 // make sure the velocity is sane (not a NaN)
2233 SV_CheckVelocity(ent);
2234 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2235 // player_run/player_stand1 does not horribly malfunction if the
2236 // velocity becomes a number that is both == 0 and != 0
2237 // (sounds to me like NaN but to be absolutely safe...)
2238 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2239 VectorClear(ent->fields.server->velocity);
2241 // perform MOVETYPE_WALK behavior
2244 // call standard player post-think, with frametime = 0
2245 prog->globals.server->time = sv.time;
2246 prog->globals.server->frametime = 0;
2247 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2248 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2249 prog->globals.server->frametime = sv.frametime;
2251 if(ent->fields.server->fixangle)
2253 // angle fixing was requested by physics code...
2254 // so store the current angles for later use
2255 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2256 host_client->fixangle_angles_set = TRUE;
2258 // and clear fixangle for the next frame
2259 ent->fields.server->fixangle = 0;
2263 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2265 // don't do physics on disconnected clients, FrikBot relies on this
2266 if (!host_client->spawned)
2268 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2272 // don't run physics here if running asynchronously
2273 if (host_client->clmovement_inputtimeout <= 0)
2276 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2279 // make sure the velocity is sane (not a NaN)
2280 SV_CheckVelocity(ent);
2281 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2282 // player_run/player_stand1 does not horribly malfunction if the
2283 // velocity becomes a number that is both == 0 and != 0
2284 // (sounds to me like NaN but to be absolutely safe...)
2285 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2286 VectorClear(ent->fields.server->velocity);
2288 // call standard client pre-think
2289 prog->globals.server->time = sv.time;
2290 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2291 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2292 SV_CheckVelocity (ent);
2294 switch ((int) ent->fields.server->movetype)
2297 case MOVETYPE_FAKEPUSH:
2298 SV_Physics_Pusher (ent);
2301 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2302 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2305 case MOVETYPE_FOLLOW:
2306 SV_Physics_Follow (ent);
2308 case MOVETYPE_NOCLIP:
2311 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2312 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2315 SV_Physics_Step (ent);
2319 // don't run physics here if running asynchronously
2320 if (host_client->clmovement_inputtimeout <= 0)
2324 case MOVETYPE_BOUNCE:
2325 case MOVETYPE_BOUNCEMISSILE:
2326 case MOVETYPE_FLYMISSILE:
2329 SV_Physics_Toss (ent);
2336 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2340 // decrement the countdown variable used to decide when to go back to
2341 // synchronous physics
2342 if (host_client->clmovement_inputtimeout > sv.frametime)
2343 host_client->clmovement_inputtimeout -= sv.frametime;
2345 host_client->clmovement_inputtimeout = 0;
2347 SV_CheckVelocity (ent);
2349 SV_LinkEdict (ent, true);
2351 SV_CheckVelocity (ent);
2353 // call standard player post-think
2354 prog->globals.server->time = sv.time;
2355 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2356 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2358 if(ent->fields.server->fixangle)
2360 // angle fixing was requested by physics code...
2361 // so store the current angles for later use
2362 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2363 host_client->fixangle_angles_set = TRUE;
2365 // and clear fixangle for the next frame
2366 ent->fields.server->fixangle = 0;
2376 void SV_Physics (void)
2381 // let the progs know that a new frame has started
2382 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2383 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2384 prog->globals.server->time = sv.time;
2385 prog->globals.server->frametime = sv.frametime;
2386 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2389 // treat each object in turn
2392 // if force_retouch, relink all the entities
2393 if (prog->globals.server->force_retouch > 0)
2394 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2395 if (!ent->priv.server->free)
2396 SV_LinkEdict (ent, true); // force retouch even for stationary
2398 // run physics on the client entities
2399 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2400 if (!ent->priv.server->free)
2401 SV_Physics_ClientEntity(ent);
2403 // run physics on all the non-client entities
2404 if (!sv_freezenonclients.integer)
2405 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2406 if (!ent->priv.server->free)
2407 SV_Physics_Entity(ent);
2409 if (prog->globals.server->force_retouch > 0)
2410 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2412 // LordHavoc: endframe support
2413 if (prog->funcoffsets.EndFrame)
2415 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2416 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2417 prog->globals.server->time = sv.time;
2418 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2421 // decrement prog->num_edicts if the highest number entities died
2422 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2424 if (!sv_freezenonclients.integer)
2425 sv.time += sv.frametime;