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)
522 VectorCopy(trace.endpos, ent->fields.server->origin);
531 void SV_CheckAllEnts (void)
536 // see if any solid entities are inside the final position
537 check = PRVM_NEXT_EDICT(prog->edicts);
538 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
540 if (check->priv.server->free)
542 if (check->fields.server->movetype == MOVETYPE_PUSH
543 || check->fields.server->movetype == MOVETYPE_NONE
544 || check->fields.server->movetype == MOVETYPE_FOLLOW
545 || check->fields.server->movetype == MOVETYPE_NOCLIP)
548 if (SV_TestEntityPosition (check, vec3_origin))
549 Con_Print("entity in invalid position\n");
553 // DRESK - Support for Entity Contents Transition Event
556 SV_CheckContentsTransition
558 returns true if entity had a valid contentstransition function call
561 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
563 int bValidFunctionCall;
564 prvm_eval_t *contentstransition;
566 // Default Valid Function Call to False
567 bValidFunctionCall = false;
569 if(ent->fields.server->watertype != nContents)
570 { // Changed Contents
571 // Acquire Contents Transition Function from QC
572 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
574 if(contentstransition->function)
575 { // Valid Function; Execute
576 // Assign Valid Function
577 bValidFunctionCall = true;
578 // Prepare Parameters (Original Contents, New Contents)
580 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
582 PRVM_G_FLOAT(OFS_PARM1) = nContents;
584 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
585 // Execute VM Function
586 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
590 // Return if Function Call was Valid
591 return bValidFunctionCall;
600 void SV_CheckVelocity (prvm_edict_t *ent)
608 for (i=0 ; i<3 ; i++)
610 if (IS_NAN(ent->fields.server->velocity[i]))
612 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
613 ent->fields.server->velocity[i] = 0;
615 if (IS_NAN(ent->fields.server->origin[i]))
617 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
618 ent->fields.server->origin[i] = 0;
622 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
623 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
624 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
626 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
627 ent->fields.server->velocity[0] *= wishspeed;
628 ent->fields.server->velocity[1] *= wishspeed;
629 ent->fields.server->velocity[2] *= wishspeed;
637 Runs thinking code if time. There is some play in the exact time the think
638 function will be called, because it is called before any movement is done
639 in a frame. Not used for pushmove objects, because they must be exact.
640 Returns false if the entity removed itself.
643 qboolean SV_RunThink (prvm_edict_t *ent)
647 // don't let things stay in the past.
648 // it is possible to start that way by a trigger with a local time.
649 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
652 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
654 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
655 ent->fields.server->nextthink = 0;
656 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
657 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
658 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
659 // mods often set nextthink to time to cause a think every frame,
660 // we don't want to loop in that case, so exit if the new nextthink is
661 // <= the time the qc was told, also exit if it is past the end of the
663 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
666 return !ent->priv.server->free;
673 Two entities have touched, so run their touch functions
676 extern void VM_SetTraceGlobals(const trace_t *trace);
677 extern sizebuf_t vm_tempstringsbuf;
678 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
680 int restorevm_tempstringsbuf_cursize;
681 int old_self, old_other;
682 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
685 old_self = prog->globals.server->self;
686 old_other = prog->globals.server->other;
687 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
689 VM_SetTraceGlobals(trace);
691 prog->globals.server->time = sv.time;
692 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
694 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
695 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
696 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
699 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
701 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
702 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
703 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
704 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
705 prog->globals.server->trace_plane_dist = -trace->plane.dist;
706 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
707 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
709 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
711 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
713 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
715 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
718 prog->globals.server->self = old_self;
719 prog->globals.server->other = old_other;
720 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
728 Slide off of the impacting object
729 returns the blocked flags (1 = floor, 2 = step / wall)
732 #define STOP_EPSILON 0.1
733 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
738 backoff = -DotProduct (in, normal) * overbounce;
739 VectorMA(in, backoff, normal, out);
741 for (i = 0;i < 3;i++)
742 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
751 The basic solid body movement clip that slides along multiple planes
752 Returns the clipflags if the velocity was modified (hit something solid)
756 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
759 static float SV_Gravity (prvm_edict_t *ent);
760 // LordHavoc: increased from 5 to 32
761 #define MAX_CLIP_PLANES 32
762 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
764 int blocked, bumpcount;
765 int i, j, impact, numplanes;
766 float d, time_left, gravity;
767 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
774 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
776 gravity = SV_Gravity(ent) * 0.5f;
777 ent->fields.server->velocity[2] -= gravity;
781 applygravity = false;
782 ent->fields.server->velocity[2] -= SV_Gravity(ent);
786 VectorCopy(ent->fields.server->velocity, original_velocity);
787 VectorCopy(ent->fields.server->velocity, primal_velocity);
790 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
792 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
795 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
796 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
798 //if (trace.fraction < 0.002)
803 VectorCopy(ent->fields.server->origin, start);
804 start[2] += 3;//0.03125;
805 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
806 end[2] += 3;//0.03125;
807 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
808 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)))
810 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
816 for (i = 0;i < numplanes;i++)
818 VectorCopy(ent->fields.server->origin, start);
819 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
820 VectorMA(start, 3, planes[i], start);
821 VectorMA(end, 3, planes[i], end);
822 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
823 if (trace.fraction < testtrace.fraction)
826 VectorCopy(start, ent->fields.server->origin);
831 // VectorAdd(ent->fields.server->origin, planes[j], start);
837 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);
838 if (trace.fraction < 1)
839 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
844 if (trace.bmodelstartsolid)
846 // LordHavoc: note: this code is what makes entities stick in place
847 // if embedded in world only (you can walk through other objects if
849 // entity is trapped in another solid
850 VectorClear(ent->fields.server->velocity);
855 // break if it moved the entire distance
856 if (trace.fraction == 1)
858 VectorCopy(trace.endpos, ent->fields.server->origin);
864 Con_Printf ("SV_FlyMove: !trace.ent");
865 trace.ent = prog->edicts;
868 impact = !((int) ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent);
870 if (trace.plane.normal[2])
872 if (trace.plane.normal[2] > 0.7)
876 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
877 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
884 // save the trace for player extrafriction
886 VectorCopy(trace.plane.normal, stepnormal);
889 if (trace.fraction >= 0.001)
891 // actually covered some distance
892 VectorCopy(trace.endpos, ent->fields.server->origin);
893 VectorCopy(ent->fields.server->velocity, original_velocity);
897 // run the impact function
900 SV_Impact(ent, &trace);
902 // break if removed by the impact function
903 if (ent->priv.server->free)
907 time_left *= 1 - trace.fraction;
909 // clipped to another plane
910 if (numplanes >= MAX_CLIP_PLANES)
912 // this shouldn't really happen
913 VectorClear(ent->fields.server->velocity);
919 for (i = 0;i < numplanes;i++)
920 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
924 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
929 VectorCopy(trace.plane.normal, planes[numplanes]);
932 if (sv_newflymove.integer)
933 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
936 // modify original_velocity so it parallels all of the clip planes
937 for (i = 0;i < numplanes;i++)
939 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
940 for (j = 0;j < numplanes;j++)
945 if (DotProduct(new_velocity, planes[j]) < 0)
955 // go along this plane
956 VectorCopy(new_velocity, ent->fields.server->velocity);
960 // go along the crease
963 VectorClear(ent->fields.server->velocity);
967 CrossProduct(planes[0], planes[1], dir);
968 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
969 VectorNormalize(dir);
970 d = DotProduct(dir, ent->fields.server->velocity);
971 VectorScale(dir, d, ent->fields.server->velocity);
975 // if current velocity is against the original velocity,
976 // stop dead to avoid tiny occilations in sloping corners
977 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
979 VectorClear(ent->fields.server->velocity);
984 //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]);
987 if ((blocked & 1) == 0 && bumpcount > 1)
989 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
990 // flag ONGROUND if there's ground under it
991 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
995 // LordHavoc: this came from QW and allows you to get out of water more easily
996 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
997 VectorCopy(primal_velocity, ent->fields.server->velocity);
998 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
999 ent->fields.server->velocity[2] -= gravity;
1009 static float SV_Gravity (prvm_edict_t *ent)
1014 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1015 if (val!=0 && val->_float)
1016 ent_gravity = val->_float;
1019 return ent_gravity * sv_gravity.value * sv.frametime;
1024 ===============================================================================
1028 ===============================================================================
1035 Does not change the entities velocity at all
1038 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
1044 VectorAdd (ent->fields.server->origin, push, end);
1046 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1047 type = MOVE_MISSILE;
1048 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1049 type = MOVE_NOMONSTERS; // only clip against bmodels
1053 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1054 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1057 VectorCopy (trace.endpos, ent->fields.server->origin);
1058 SV_LinkEdict (ent, true);
1060 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)))
1061 SV_Impact (ent, &trace);
1072 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1075 int pusherowner, pusherprog;
1078 float savesolid, movetime2, pushltime;
1079 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1081 int numcheckentities;
1082 static prvm_edict_t *checkentities[MAX_EDICTS];
1083 dp_model_t *pushermodel;
1085 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1086 unsigned short moved_edicts[MAX_EDICTS];
1088 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])
1090 pusher->fields.server->ltime += movetime;
1094 switch ((int) pusher->fields.server->solid)
1096 // LordHavoc: valid pusher types
1099 case SOLID_SLIDEBOX:
1100 case SOLID_CORPSE: // LordHavoc: this would be weird...
1102 // LordHavoc: no collisions
1105 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1106 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1107 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1108 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1109 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1110 pusher->fields.server->ltime += movetime;
1111 SV_LinkEdict (pusher, false);
1114 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1117 index = (int) pusher->fields.server->modelindex;
1118 if (index < 1 || index >= MAX_MODELS)
1120 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1123 pushermodel = sv.models[index];
1124 pusherowner = pusher->fields.server->owner;
1125 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1127 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1129 movetime2 = movetime;
1130 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1131 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1132 if (moveangle[0] || moveangle[2])
1134 for (i = 0;i < 3;i++)
1138 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1139 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1143 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1144 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1148 else if (moveangle[1])
1150 for (i = 0;i < 3;i++)
1154 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1155 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1159 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1160 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1166 for (i = 0;i < 3;i++)
1170 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1171 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1175 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1176 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1181 VectorNegate (moveangle, a);
1182 AngleVectorsFLU (a, forward, left, up);
1184 VectorCopy (pusher->fields.server->origin, pushorig);
1185 VectorCopy (pusher->fields.server->angles, pushang);
1186 pushltime = pusher->fields.server->ltime;
1188 // move the pusher to its final position
1190 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1191 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1192 pusher->fields.server->ltime += movetime;
1193 SV_LinkEdict (pusher, false);
1196 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1197 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1198 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);
1199 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1201 savesolid = pusher->fields.server->solid;
1203 // see if any solid entities are inside the final position
1206 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1207 for (e = 0;e < numcheckentities;e++)
1209 prvm_edict_t *check = checkentities[e];
1210 if (check->fields.server->movetype == MOVETYPE_NONE
1211 || check->fields.server->movetype == MOVETYPE_PUSH
1212 || check->fields.server->movetype == MOVETYPE_FOLLOW
1213 || check->fields.server->movetype == MOVETYPE_NOCLIP
1214 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1217 if (check->fields.server->owner == pusherprog)
1220 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1223 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1225 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1226 check->priv.server->waterposition_forceupdate = true;
1228 checkcontents = SV_GenericHitSuperContentsMask(check);
1230 // if the entity is standing on the pusher, it will definitely be moved
1231 // if the entity is not standing on the pusher, but is in the pusher's
1232 // final position, move it
1233 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1235 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);
1236 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1237 if (!trace.startsolid)
1239 //Con_Printf("- not in solid\n");
1247 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1248 org2[0] = DotProduct (org, forward);
1249 org2[1] = DotProduct (org, left);
1250 org2[2] = DotProduct (org, up);
1251 VectorSubtract (org2, org, move);
1252 VectorAdd (move, move1, move);
1255 VectorCopy (move1, move);
1257 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1259 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1260 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1261 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1263 // try moving the contacted entity
1264 pusher->fields.server->solid = SOLID_NOT;
1265 trace = SV_PushEntity (check, move, true);
1266 // FIXME: turn players specially
1267 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1268 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1269 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1271 // this trace.fraction < 1 check causes items to fall off of pushers
1272 // if they pass under or through a wall
1273 // the groundentity check causes items to fall off of ledges
1274 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1275 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1277 // if it is still inside the pusher, block
1278 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);
1279 if (trace.startsolid)
1281 // try moving the contacted entity a tiny bit further to account for precision errors
1283 pusher->fields.server->solid = SOLID_NOT;
1284 VectorScale(move, 1.1, move2);
1285 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1286 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1287 SV_PushEntity (check, move2, true);
1288 pusher->fields.server->solid = savesolid;
1289 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);
1290 if (trace.startsolid)
1292 // try moving the contacted entity a tiny bit less to account for precision errors
1293 pusher->fields.server->solid = SOLID_NOT;
1294 VectorScale(move, 0.9, move2);
1295 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1296 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1297 SV_PushEntity (check, move2, true);
1298 pusher->fields.server->solid = savesolid;
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 // still inside pusher, so it's really blocked
1305 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1307 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1310 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1311 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1315 VectorCopy (pushorig, pusher->fields.server->origin);
1316 VectorCopy (pushang, pusher->fields.server->angles);
1317 pusher->fields.server->ltime = pushltime;
1318 SV_LinkEdict (pusher, false);
1320 // move back any entities we already moved
1321 for (i = 0;i < num_moved;i++)
1323 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1324 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1325 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1326 SV_LinkEdict (ed, false);
1329 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1330 if (pusher->fields.server->blocked)
1332 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1333 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1334 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1341 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1342 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1343 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1352 void SV_Physics_Pusher (prvm_edict_t *ent)
1354 float thinktime, oldltime, movetime;
1356 oldltime = ent->fields.server->ltime;
1358 thinktime = ent->fields.server->nextthink;
1359 if (thinktime < ent->fields.server->ltime + sv.frametime)
1361 movetime = thinktime - ent->fields.server->ltime;
1366 movetime = sv.frametime;
1369 // advances ent->fields.server->ltime if not blocked
1370 SV_PushMove (ent, movetime);
1372 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1374 ent->fields.server->nextthink = 0;
1375 prog->globals.server->time = sv.time;
1376 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1377 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1378 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1384 ===============================================================================
1388 ===============================================================================
1391 static float unstickoffsets[] =
1441 This is a big hack to try and fix the rare case of getting stuck in the world
1445 void SV_CheckStuck (prvm_edict_t *ent)
1450 if (!SV_TestEntityPosition(ent, vec3_origin))
1452 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1456 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1458 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1460 Con_DPrintf("Unstuck player entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1461 SV_LinkEdict (ent, true);
1466 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1467 if (!SV_TestEntityPosition(ent, offset))
1469 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1470 SV_LinkEdict (ent, true);
1474 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1477 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1481 // if not stuck in a bmodel, just return
1482 if (!SV_TestEntityPosition(ent, vec3_origin))
1485 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1487 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1489 Con_DPrintf("Unstuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname), unstickoffsets[i+0], unstickoffsets[i+1], unstickoffsets[i+2]);
1490 SV_LinkEdict (ent, true);
1495 if (developer.integer >= 100)
1496 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1506 qboolean SV_CheckWater (prvm_edict_t *ent)
1509 int nNativeContents;
1512 point[0] = ent->fields.server->origin[0];
1513 point[1] = ent->fields.server->origin[1];
1514 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1516 // DRESK - Support for Entity Contents Transition Event
1517 // NOTE: Some logic needed to be slightly re-ordered
1518 // to not affect performance and allow for the feature.
1520 // Acquire Super Contents Prior to Resets
1521 cont = SV_PointSuperContents(point);
1522 // Acquire Native Contents Here
1523 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1525 // DRESK - Support for Entity Contents Transition Event
1526 if(ent->fields.server->watertype)
1527 // Entity did NOT Spawn; Check
1528 SV_CheckContentsTransition(ent, nNativeContents);
1531 ent->fields.server->waterlevel = 0;
1532 ent->fields.server->watertype = CONTENTS_EMPTY;
1533 cont = SV_PointSuperContents(point);
1534 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1536 ent->fields.server->watertype = nNativeContents;
1537 ent->fields.server->waterlevel = 1;
1538 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1539 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1541 ent->fields.server->waterlevel = 2;
1542 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1543 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1544 ent->fields.server->waterlevel = 3;
1548 return ent->fields.server->waterlevel > 1;
1557 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1560 vec3_t forward, into, side;
1562 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1563 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1565 // cut the tangential velocity
1566 i = DotProduct (stepnormal, ent->fields.server->velocity);
1567 VectorScale (stepnormal, i, into);
1568 VectorSubtract (ent->fields.server->velocity, into, side);
1569 ent->fields.server->velocity[0] = side[0] * (1 + d);
1570 ent->fields.server->velocity[1] = side[1] * (1 + d);
1576 =====================
1579 Player has come to a dead stop, possibly due to the problem with limited
1580 float precision at some angle joins in the BSP hull.
1582 Try fixing by pushing one pixel in each direction.
1584 This is a hack, but in the interest of good gameplay...
1585 ======================
1587 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1592 VectorCopy (ent->fields.server->origin, oldorg);
1595 for (i=0 ; i<8 ; i++)
1597 // try pushing a little in an axial direction
1600 case 0: dir[0] = 2; dir[1] = 0; break;
1601 case 1: dir[0] = 0; dir[1] = 2; break;
1602 case 2: dir[0] = -2; dir[1] = 0; break;
1603 case 3: dir[0] = 0; dir[1] = -2; break;
1604 case 4: dir[0] = 2; dir[1] = 2; break;
1605 case 5: dir[0] = -2; dir[1] = 2; break;
1606 case 6: dir[0] = 2; dir[1] = -2; break;
1607 case 7: dir[0] = -2; dir[1] = -2; break;
1610 SV_PushEntity (ent, dir, false);
1612 // retry the original move
1613 ent->fields.server->velocity[0] = oldvel[0];
1614 ent->fields.server->velocity[1] = oldvel[1];
1615 ent->fields.server->velocity[2] = 0;
1616 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1618 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1619 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1621 Con_DPrint("TryUnstick - success.\n");
1625 // go back to the original pos and try again
1626 VectorCopy (oldorg, ent->fields.server->origin);
1630 VectorClear (ent->fields.server->velocity);
1631 Con_DPrint("TryUnstick - failure.\n");
1637 =====================
1640 Only used by players
1641 ======================
1643 void SV_WalkMove (prvm_edict_t *ent)
1645 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1646 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1648 qboolean applygravity;
1650 // if frametime is 0 (due to client sending the same timestamp twice),
1652 if (sv.frametime <= 0)
1655 SV_CheckStuck (ent);
1657 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1659 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1661 SV_CheckVelocity(ent);
1663 // do a regular slide move unless it looks like you ran into a step
1664 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1666 VectorCopy (ent->fields.server->origin, start_origin);
1667 VectorCopy (ent->fields.server->velocity, start_velocity);
1669 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1671 // if the move did not hit the ground at any point, we're not on ground
1673 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1675 SV_CheckVelocity(ent);
1676 SV_LinkEdict (ent, true);
1678 VectorCopy(ent->fields.server->origin, originalmove_origin);
1679 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1680 originalmove_clip = clip;
1681 originalmove_flags = (int)ent->fields.server->flags;
1682 originalmove_groundentity = ent->fields.server->groundentity;
1684 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1687 if (sv_nostep.integer)
1690 // if move didn't block on a step, return
1693 // if move was not trying to move into the step, return
1694 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1697 if (ent->fields.server->movetype != MOVETYPE_FLY)
1699 // return if gibbed by a trigger
1700 if (ent->fields.server->movetype != MOVETYPE_WALK)
1703 // only step up while jumping if that is enabled
1704 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1705 if (!oldonground && ent->fields.server->waterlevel == 0)
1709 // try moving up and forward to go up a step
1710 // back to start pos
1711 VectorCopy (start_origin, ent->fields.server->origin);
1712 VectorCopy (start_velocity, ent->fields.server->velocity);
1715 VectorClear (upmove);
1716 upmove[2] = sv_stepheight.value;
1717 // FIXME: don't link?
1718 SV_PushEntity(ent, upmove, false);
1721 ent->fields.server->velocity[2] = 0;
1722 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1723 ent->fields.server->velocity[2] += start_velocity[2];
1725 SV_CheckVelocity(ent);
1726 SV_LinkEdict (ent, true);
1728 // check for stuckness, possibly due to the limited precision of floats
1729 // in the clipping hulls
1731 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1732 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1734 //Con_Printf("wall\n");
1735 // stepping up didn't make any progress, revert to original move
1736 VectorCopy(originalmove_origin, ent->fields.server->origin);
1737 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1738 //clip = originalmove_clip;
1739 ent->fields.server->flags = originalmove_flags;
1740 ent->fields.server->groundentity = originalmove_groundentity;
1741 // now try to unstick if needed
1742 //clip = SV_TryUnstick (ent, oldvel);
1746 //Con_Printf("step - ");
1748 // extra friction based on view angle
1749 if (clip & 2 && sv_wallfriction.integer)
1750 SV_WallFriction (ent, stepnormal);
1752 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1753 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))
1757 VectorClear (downmove);
1758 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1759 // FIXME: don't link?
1760 downtrace = SV_PushEntity (ent, downmove, false);
1762 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1764 // this has been disabled so that you can't jump when you are stepping
1765 // up while already jumping (also known as the Quake2 double jump bug)
1767 // LordHavoc: disabled this check so you can walk on monsters/players
1768 //if (ent->fields.server->solid == SOLID_BSP)
1770 //Con_Printf("onground\n");
1771 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1772 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1778 //Con_Printf("slope\n");
1779 // if the push down didn't end up on good ground, use the move without
1780 // the step up. This happens near wall / slope combinations, and can
1781 // cause the player to hop up higher on a slope too steep to climb
1782 VectorCopy(originalmove_origin, ent->fields.server->origin);
1783 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1784 //clip = originalmove_clip;
1785 ent->fields.server->flags = originalmove_flags;
1786 ent->fields.server->groundentity = originalmove_groundentity;
1789 SV_CheckVelocity(ent);
1790 SV_LinkEdict (ent, true);
1793 //============================================================================
1799 Entities that are "stuck" to another entity
1802 void SV_Physics_Follow (prvm_edict_t *ent)
1804 vec3_t vf, vr, vu, angles, v;
1808 if (!SV_RunThink (ent))
1811 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1812 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1813 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])
1815 // quick case for no rotation
1816 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1820 angles[0] = -ent->fields.server->punchangle[0];
1821 angles[1] = ent->fields.server->punchangle[1];
1822 angles[2] = ent->fields.server->punchangle[2];
1823 AngleVectors (angles, vf, vr, vu);
1824 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];
1825 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];
1826 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];
1827 angles[0] = -e->fields.server->angles[0];
1828 angles[1] = e->fields.server->angles[1];
1829 angles[2] = e->fields.server->angles[2];
1830 AngleVectors (angles, vf, vr, vu);
1831 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1832 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1833 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1835 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1836 SV_LinkEdict (ent, true);
1840 ==============================================================================
1844 ==============================================================================
1849 SV_CheckWaterTransition
1853 void SV_CheckWaterTransition (prvm_edict_t *ent)
1856 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1857 if (!ent->fields.server->watertype)
1859 // just spawned here
1860 ent->fields.server->watertype = cont;
1861 ent->fields.server->waterlevel = 1;
1865 // DRESK - Support for Entity Contents Transition Event
1866 // NOTE: Call here BEFORE updating the watertype below,
1867 // and suppress watersplash sound if a valid function
1868 // call was made to allow for custom "splash" sounds.
1869 if( !SV_CheckContentsTransition(ent, cont) )
1870 { // Contents Transition Function Invalid; Potentially Play Water Sound
1871 // check if the entity crossed into or out of water
1872 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1873 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1876 if (cont <= CONTENTS_WATER)
1878 ent->fields.server->watertype = cont;
1879 ent->fields.server->waterlevel = 1;
1883 ent->fields.server->watertype = CONTENTS_EMPTY;
1884 ent->fields.server->waterlevel = 0;
1892 Toss, bounce, and fly movement. When onground, do nothing.
1895 void SV_Physics_Toss (prvm_edict_t *ent)
1902 // if onground, return without moving
1903 if ((int)ent->fields.server->flags & FL_ONGROUND)
1905 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1907 // don't stick to ground if onground and moving upward
1908 ent->fields.server->flags -= FL_ONGROUND;
1910 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1912 // we can trust FL_ONGROUND if groundentity is world because it never moves
1915 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1917 // if ent was supported by a brush model on previous frame,
1918 // and groundentity is now freed, set groundentity to 0 (world)
1919 // which leaves it suspended in the air
1920 ent->fields.server->groundentity = 0;
1924 ent->priv.server->suspendedinairflag = false;
1926 SV_CheckVelocity (ent);
1929 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1930 ent->fields.server->velocity[2] -= SV_Gravity(ent);
1933 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1935 movetime = sv.frametime;
1936 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
1939 VectorScale (ent->fields.server->velocity, movetime, move);
1940 trace = SV_PushEntity (ent, move, true);
1941 if (ent->priv.server->free)
1943 if (trace.bmodelstartsolid)
1945 // try to unstick the entity
1946 SV_UnstickEntity(ent);
1947 trace = SV_PushEntity (ent, move, false);
1948 if (ent->priv.server->free)
1951 if (trace.fraction == 1)
1953 movetime *= 1 - min(1, trace.fraction);
1954 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1956 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1957 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1959 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1961 float d, ent_gravity;
1963 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1964 // LordHavoc: fixed grenades not bouncing when fired down a slope
1965 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1966 if (val!=0 && val->_float)
1967 ent_gravity = val->_float;
1970 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1972 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1973 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * (60.0 / 800.0) * ent_gravity)
1975 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1976 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1977 VectorClear (ent->fields.server->velocity);
1978 VectorClear (ent->fields.server->avelocity);
1981 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1985 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * (60.0 / 800.0) * ent_gravity)
1987 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1988 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1989 VectorClear (ent->fields.server->velocity);
1990 VectorClear (ent->fields.server->avelocity);
1993 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1998 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1999 if (trace.plane.normal[2] > 0.7)
2001 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2002 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2003 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2004 ent->priv.server->suspendedinairflag = true;
2005 VectorClear (ent->fields.server->velocity);
2006 VectorClear (ent->fields.server->avelocity);
2009 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2011 if (!sv_gameplayfix_slidemoveprojectiles.integer)
2015 // check for in water
2016 SV_CheckWaterTransition (ent);
2020 ===============================================================================
2024 ===============================================================================
2031 Monsters freefall when they don't have a ground entity, otherwise
2032 all movement is done with discrete steps.
2034 This is also used for objects that have become still on the ground, but
2035 will fall if the floor is pulled out from under them.
2038 void SV_Physics_Step (prvm_edict_t *ent)
2040 int flags = (int)ent->fields.server->flags;
2043 // Backup Velocity in the event that movetypesteplandevent is called,
2044 // to provide a parameter with the entity's velocity at impact.
2045 prvm_eval_t *movetypesteplandevent;
2046 vec3_t backupVelocity;
2047 VectorCopy(ent->fields.server->velocity, backupVelocity);
2048 // don't fall at all if fly/swim
2049 if (!(flags & (FL_FLY | FL_SWIM)))
2051 if (flags & FL_ONGROUND)
2053 // freefall if onground and moving upward
2054 // freefall if not standing on a world surface (it may be a lift or trap door)
2055 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2057 ent->fields.server->flags -= FL_ONGROUND;
2058 SV_CheckVelocity(ent);
2059 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2060 SV_LinkEdict(ent, true);
2061 ent->priv.server->waterposition_forceupdate = true;
2066 // freefall if not onground
2067 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2069 SV_CheckVelocity(ent);
2070 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2071 SV_LinkEdict(ent, true);
2074 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2076 // DRESK - Check for Entity Land Event Function
2077 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2079 if(movetypesteplandevent->function)
2080 { // Valid Function; Execute
2081 // Prepare Parameters
2082 // Assign Velocity at Impact
2083 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2084 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2085 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2087 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2088 // Execute VM Function
2089 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2092 // Check for Engine Landing Sound
2093 if(sv_sound_land.string)
2094 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2096 ent->priv.server->waterposition_forceupdate = true;
2101 if (!SV_RunThink(ent))
2104 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2106 ent->priv.server->waterposition_forceupdate = false;
2107 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2108 SV_CheckWaterTransition(ent);
2112 //============================================================================
2114 static void SV_Physics_Entity (prvm_edict_t *ent)
2116 // don't run think/move on newly spawned projectiles as it messes up
2117 // movement interpolation and rocket trails, and is inconsistent with
2118 // respect to entities spawned in the same frame
2119 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2120 // but if it spawns a lower numbered ent, it doesn't - this never moves
2121 // ents in the first frame regardless)
2122 qboolean runmove = ent->priv.server->move;
2123 ent->priv.server->move = true;
2124 if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2126 switch ((int) ent->fields.server->movetype)
2129 case MOVETYPE_FAKEPUSH:
2130 SV_Physics_Pusher (ent);
2133 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2134 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2137 case MOVETYPE_FOLLOW:
2138 SV_Physics_Follow (ent);
2140 case MOVETYPE_NOCLIP:
2141 if (SV_RunThink(ent))
2144 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2145 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2147 SV_LinkEdict(ent, false);
2150 SV_Physics_Step (ent);
2153 if (SV_RunThink (ent))
2157 case MOVETYPE_BOUNCE:
2158 case MOVETYPE_BOUNCEMISSILE:
2159 case MOVETYPE_FLYMISSILE:
2162 if (SV_RunThink (ent))
2163 SV_Physics_Toss (ent);
2166 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2171 void SV_Physics_ClientMove(void)
2174 ent = host_client->edict;
2176 // call player physics, this needs the proper frametime
2177 prog->globals.server->frametime = sv.frametime;
2180 // call standard client pre-think, with frametime = 0
2181 prog->globals.server->time = sv.time;
2182 prog->globals.server->frametime = 0;
2183 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2184 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2185 prog->globals.server->frametime = sv.frametime;
2187 // make sure the velocity is sane (not a NaN)
2188 SV_CheckVelocity(ent);
2189 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2190 // player_run/player_stand1 does not horribly malfunction if the
2191 // velocity becomes a number that is both == 0 and != 0
2192 // (sounds to me like NaN but to be absolutely safe...)
2193 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2194 VectorClear(ent->fields.server->velocity);
2196 // perform MOVETYPE_WALK behavior
2199 // call standard player post-think, with frametime = 0
2200 prog->globals.server->time = sv.time;
2201 prog->globals.server->frametime = 0;
2202 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2203 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2204 prog->globals.server->frametime = sv.frametime;
2206 if(ent->fields.server->fixangle)
2208 // angle fixing was requested by physics code...
2209 // so store the current angles for later use
2210 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2211 host_client->fixangle_angles_set = TRUE;
2213 // and clear fixangle for the next frame
2214 ent->fields.server->fixangle = 0;
2218 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2220 // don't do physics on disconnected clients, FrikBot relies on this
2221 if (!host_client->spawned)
2223 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2227 // don't run physics here if running asynchronously
2228 if (host_client->clmovement_skipphysicsframes <= 0)
2231 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2234 // make sure the velocity is sane (not a NaN)
2235 SV_CheckVelocity(ent);
2236 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2237 // player_run/player_stand1 does not horribly malfunction if the
2238 // velocity becomes a number that is both == 0 and != 0
2239 // (sounds to me like NaN but to be absolutely safe...)
2240 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2241 VectorClear(ent->fields.server->velocity);
2243 // call standard client pre-think
2244 prog->globals.server->time = sv.time;
2245 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2246 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2247 SV_CheckVelocity (ent);
2249 switch ((int) ent->fields.server->movetype)
2252 case MOVETYPE_FAKEPUSH:
2253 SV_Physics_Pusher (ent);
2256 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2257 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2260 case MOVETYPE_FOLLOW:
2261 SV_Physics_Follow (ent);
2263 case MOVETYPE_NOCLIP:
2266 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2267 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2270 SV_Physics_Step (ent);
2274 // don't run physics here if running asynchronously
2275 if (host_client->clmovement_skipphysicsframes <= 0)
2279 case MOVETYPE_BOUNCE:
2280 case MOVETYPE_BOUNCEMISSILE:
2281 case MOVETYPE_FLYMISSILE:
2284 SV_Physics_Toss (ent);
2291 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2295 // decrement the countdown variable used to decide when to go back to
2296 // synchronous physics
2297 if (host_client->clmovement_skipphysicsframes > 0)
2298 host_client->clmovement_skipphysicsframes--;
2300 SV_CheckVelocity (ent);
2302 SV_LinkEdict (ent, true);
2304 SV_CheckVelocity (ent);
2306 // call standard player post-think
2307 prog->globals.server->time = sv.time;
2308 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2309 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2311 if(ent->fields.server->fixangle)
2313 // angle fixing was requested by physics code...
2314 // so store the current angles for later use
2315 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2316 host_client->fixangle_angles_set = TRUE;
2318 // and clear fixangle for the next frame
2319 ent->fields.server->fixangle = 0;
2329 void SV_Physics (void)
2334 // let the progs know that a new frame has started
2335 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2336 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2337 prog->globals.server->time = sv.time;
2338 prog->globals.server->frametime = sv.frametime;
2339 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2342 // treat each object in turn
2345 // if force_retouch, relink all the entities
2346 if (prog->globals.server->force_retouch > 0)
2347 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2348 if (!ent->priv.server->free)
2349 SV_LinkEdict (ent, true); // force retouch even for stationary
2351 // run physics on the client entities
2352 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2353 if (!ent->priv.server->free)
2354 SV_Physics_ClientEntity(ent);
2356 // run physics on all the non-client entities
2357 if (!sv_freezenonclients.integer)
2358 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2359 if (!ent->priv.server->free)
2360 SV_Physics_Entity(ent);
2362 if (prog->globals.server->force_retouch > 0)
2363 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2365 // LordHavoc: endframe support
2366 if (prog->funcoffsets.EndFrame)
2368 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2369 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2370 prog->globals.server->time = sv.time;
2371 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2374 // decrement prog->num_edicts if the highest number entities died
2375 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2377 if (!sv_freezenonclients.integer)
2378 sv.time += sv.frametime;