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 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
86 #if COLLISIONPARANOID >= 1
87 trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
89 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t pEnd, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
92 #if COLLISIONPARANOID >= 1
93 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)
95 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)
99 vec3_t hullmins, hullmaxs;
100 int i, bodysupercontents;
104 prvm_edict_t *traceowner, *touch;
106 // bounding box of entire move area
107 vec3_t clipboxmins, clipboxmaxs;
108 // size of the moving object
109 vec3_t clipmins, clipmaxs;
110 // size when clipping against monsters
111 vec3_t clipmins2, clipmaxs2;
112 // start and end origin of move
113 vec3_t clipstart, clipend;
116 // matrices to transform into/out of other entity's space
117 matrix4x4_t matrix, imatrix;
118 // model of other entity
120 // list of entities to test for collisions
122 prvm_edict_t *touchedicts[MAX_EDICTS];
123 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
127 if(!VectorCompare(start, pEnd))
129 // TRICK: make the trace 1 qu longer!
130 VectorSubtract(pEnd, start, end);
131 len = VectorNormalizeLength(end);
132 VectorAdd(pEnd, end, end);
135 VectorCopy(pEnd, end);
138 VectorCopy(start, clipstart);
139 VectorCopy(end, clipend);
140 VectorCopy(mins, clipmins);
141 VectorCopy(maxs, clipmaxs);
142 VectorCopy(mins, clipmins2);
143 VectorCopy(maxs, clipmaxs2);
144 #if COLLISIONPARANOID >= 3
145 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
149 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
150 cliptrace.bmodelstartsolid = cliptrace.startsolid;
151 if (cliptrace.startsolid || cliptrace.fraction < 1)
152 cliptrace.ent = prog->edicts;
153 if (type == MOVE_WORLDONLY)
156 if (type == MOVE_MISSILE)
158 // LordHavoc: modified this, was = -15, now -= 15
159 for (i = 0;i < 3;i++)
166 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
167 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
168 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
171 VectorCopy(clipmins, hullmins);
172 VectorCopy(clipmaxs, hullmaxs);
175 // create the bounding box of the entire move
176 for (i = 0;i < 3;i++)
178 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
179 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
182 // debug override to test against everything
183 if (sv_debugmove.integer)
185 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
186 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
189 // if the passedict is world, make it NULL (to avoid two checks each time)
190 if (passedict == prog->edicts)
192 // precalculate prog value for passedict for comparisons
193 passedictprog = PRVM_EDICT_TO_PROG(passedict);
194 // figure out whether this is a point trace for comparisons
195 pointtrace = VectorCompare(clipmins, clipmaxs);
196 // precalculate passedict's owner edict pointer for comparisons
197 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
200 // because this uses World_EntitiestoBox, we know all entity boxes overlap
201 // the clip region, so we can skip culling checks in the loop below
202 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
203 if (numtouchedicts > MAX_EDICTS)
205 // this never happens
206 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
207 numtouchedicts = MAX_EDICTS;
209 for (i = 0;i < numtouchedicts;i++)
211 touch = touchedicts[i];
213 if (touch->fields.server->solid < SOLID_BBOX)
215 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
220 // don't clip against self
221 if (passedict == touch)
223 // don't clip owned entities against owner
224 if (traceowner == touch)
226 // don't clip owner against owned entities
227 if (passedictprog == touch->fields.server->owner)
229 // don't clip points against points (they can't collide)
230 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
234 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
236 // might interact, so do an exact clip
238 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
240 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
241 // if the modelindex is 0, it shouldn't be SOLID_BSP!
242 if (modelindex > 0 && modelindex < MAX_MODELS)
243 model = sv.models[(int)touch->fields.server->modelindex];
246 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
248 model->type == mod_alias
251 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
253 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
259 Matrix4x4_CreateFromQuakeEntity(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2], pitchsign * touch->fields.server->angles[0], touch->fields.server->angles[1], touch->fields.server->angles[2], 1);
261 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
262 Matrix4x4_Invert_Simple(&imatrix, &matrix);
263 if ((int)touch->fields.server->flags & FL_MONSTER)
264 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);
266 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);
268 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
272 #ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND
273 if(!VectorCompare(start, pEnd))
274 Collision_ShortenTrace(&cliptrace, len / (len + 1), pEnd);
279 #if COLLISIONPARANOID >= 1
280 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)
285 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
288 VectorCopy(trace.endpos, temp);
289 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
290 #if COLLISIONPARANOID < 3
291 if (trace.startsolid || endstuck)
293 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" : "");
299 int SV_PointSuperContents(const vec3_t point)
301 int supercontents = 0;
305 // matrices to transform into/out of other entity's space
306 matrix4x4_t matrix, imatrix;
307 // model of other entity
309 unsigned int modelindex;
311 // list of entities to test for collisions
313 prvm_edict_t *touchedicts[MAX_EDICTS];
315 // get world supercontents at this point
316 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
317 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
319 // if sv_gameplayfix_swiminbmodels is off we're done
320 if (!sv_gameplayfix_swiminbmodels.integer)
321 return supercontents;
323 // get list of entities at this point
324 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
325 if (numtouchedicts > MAX_EDICTS)
327 // this never happens
328 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
329 numtouchedicts = MAX_EDICTS;
331 for (i = 0;i < numtouchedicts;i++)
333 touch = touchedicts[i];
335 // we only care about SOLID_BSP for pointcontents
336 if (touch->fields.server->solid != SOLID_BSP)
339 // might interact, so do an exact clip
340 modelindex = (unsigned int)touch->fields.server->modelindex;
341 if (modelindex >= MAX_MODELS)
343 model = sv.models[(int)touch->fields.server->modelindex];
344 if (!model || !model->PointSuperContents)
346 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);
347 Matrix4x4_Invert_Simple(&imatrix, &matrix);
348 Matrix4x4_Transform(&imatrix, point, transformed);
349 frame = (int)touch->fields.server->frame;
350 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
353 return supercontents;
357 ===============================================================================
359 Linking entities into the world culling system
361 ===============================================================================
364 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
366 int i, numtouchedicts, old_self, old_other;
367 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
369 // build a list of edicts to touch, because the link loop can be corrupted
370 // by IncreaseEdicts called during touch functions
371 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
372 if (numtouchedicts > MAX_EDICTS)
374 // this never happens
375 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
376 numtouchedicts = MAX_EDICTS;
379 old_self = prog->globals.server->self;
380 old_other = prog->globals.server->other;
381 for (i = 0;i < numtouchedicts;i++)
383 touch = touchedicts[i];
384 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
387 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
388 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
389 prog->globals.server->time = sv.time;
390 prog->globals.server->trace_allsolid = false;
391 prog->globals.server->trace_startsolid = false;
392 prog->globals.server->trace_fraction = 1;
393 prog->globals.server->trace_inwater = false;
394 prog->globals.server->trace_inopen = true;
395 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
396 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
397 prog->globals.server->trace_plane_dist = 0;
398 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
399 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
401 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
403 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
405 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
407 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
410 prog->globals.server->self = old_self;
411 prog->globals.server->other = old_other;
420 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
425 if (ent == prog->edicts)
426 return; // don't add the world
428 if (ent->priv.server->free)
433 if (ent->fields.server->solid == SOLID_BSP)
435 int modelindex = (int)ent->fields.server->modelindex;
436 if (modelindex < 0 || modelindex >= MAX_MODELS)
438 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
441 model = sv.models[modelindex];
444 if (!model->TraceBox && developer.integer >= 1)
445 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
447 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
449 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
450 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
452 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
454 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
455 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
459 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
460 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
465 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
466 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
467 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
472 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
473 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
477 // to make items easier to pick up and allow them to be grabbed off
478 // of shelves, the abs sizes are expanded
480 if ((int)ent->fields.server->flags & FL_ITEM)
491 // because movement is clipped an epsilon away from an actual edge,
492 // we must fully check even when bounding boxes don't quite touch
501 VectorCopy(mins, ent->fields.server->absmin);
502 VectorCopy(maxs, ent->fields.server->absmax);
504 World_LinkEdict(&sv.world, ent, mins, maxs);
506 // if touch_triggers, call touch on all entities overlapping this box
507 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
508 SV_LinkEdict_TouchAreaGrid(ent);
512 ===============================================================================
516 ===============================================================================
521 SV_TestEntityPosition
523 returns true if the entity is in solid currently
526 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
531 contents = SV_GenericHitSuperContentsMask(ent);
532 VectorAdd(ent->fields.server->origin, offset, org);
533 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
534 if (trace.startsupercontents & contents)
538 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
540 // q1bsp/hlbsp use hulls and if the entity does not exactly match
541 // a hull size it is incorrectly tested, so this code tries to
542 // 'fix' it slightly...
543 // FIXME: this breaks entities larger than the hull size
546 VectorAdd(org, ent->fields.server->mins, m1);
547 VectorAdd(org, ent->fields.server->maxs, m2);
548 VectorSubtract(m2, m1, s);
549 #define EPSILON (1.0f / 32.0f)
550 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
551 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
552 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
553 for (i = 0;i < 8;i++)
555 v[0] = (i & 1) ? m2[0] : m1[0];
556 v[1] = (i & 2) ? m2[1] : m1[1];
557 v[2] = (i & 4) ? m2[2] : m1[2];
558 if (SV_PointSuperContents(v) & contents)
563 // if the trace found a better position for the entity, move it there
564 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
567 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
568 VectorCopy(trace.endpos, ent->fields.server->origin);
570 // verify if the endpos is REALLY outside solid
571 VectorCopy(trace.endpos, org);
572 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
574 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
576 VectorCopy(org, ent->fields.server->origin);
587 void SV_CheckAllEnts (void)
592 // see if any solid entities are inside the final position
593 check = PRVM_NEXT_EDICT(prog->edicts);
594 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
596 if (check->priv.server->free)
598 if (check->fields.server->movetype == MOVETYPE_PUSH
599 || check->fields.server->movetype == MOVETYPE_NONE
600 || check->fields.server->movetype == MOVETYPE_FOLLOW
601 || check->fields.server->movetype == MOVETYPE_NOCLIP)
604 if (SV_TestEntityPosition (check, vec3_origin))
605 Con_Print("entity in invalid position\n");
609 // DRESK - Support for Entity Contents Transition Event
612 SV_CheckContentsTransition
614 returns true if entity had a valid contentstransition function call
617 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
619 int bValidFunctionCall;
620 prvm_eval_t *contentstransition;
622 // Default Valid Function Call to False
623 bValidFunctionCall = false;
625 if(ent->fields.server->watertype != nContents)
626 { // Changed Contents
627 // Acquire Contents Transition Function from QC
628 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
630 if(contentstransition->function)
631 { // Valid Function; Execute
632 // Assign Valid Function
633 bValidFunctionCall = true;
634 // Prepare Parameters (Original Contents, New Contents)
636 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
638 PRVM_G_FLOAT(OFS_PARM1) = nContents;
640 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
641 // Execute VM Function
642 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
646 // Return if Function Call was Valid
647 return bValidFunctionCall;
656 void SV_CheckVelocity (prvm_edict_t *ent)
664 for (i=0 ; i<3 ; i++)
666 if (IS_NAN(ent->fields.server->velocity[i]))
668 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
669 ent->fields.server->velocity[i] = 0;
671 if (IS_NAN(ent->fields.server->origin[i]))
673 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
674 ent->fields.server->origin[i] = 0;
678 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
679 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
680 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
682 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
683 ent->fields.server->velocity[0] *= wishspeed;
684 ent->fields.server->velocity[1] *= wishspeed;
685 ent->fields.server->velocity[2] *= wishspeed;
693 Runs thinking code if time. There is some play in the exact time the think
694 function will be called, because it is called before any movement is done
695 in a frame. Not used for pushmove objects, because they must be exact.
696 Returns false if the entity removed itself.
699 qboolean SV_RunThink (prvm_edict_t *ent)
703 // don't let things stay in the past.
704 // it is possible to start that way by a trigger with a local time.
705 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
708 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
710 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
711 ent->fields.server->nextthink = 0;
712 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
713 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
714 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
715 // mods often set nextthink to time to cause a think every frame,
716 // we don't want to loop in that case, so exit if the new nextthink is
717 // <= the time the qc was told, also exit if it is past the end of the
719 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
722 return !ent->priv.server->free;
729 Two entities have touched, so run their touch functions
730 returns true if the impact kept the origin of the touching entity intact
733 extern void VM_SetTraceGlobals(const trace_t *trace);
734 extern sizebuf_t vm_tempstringsbuf;
735 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
737 int restorevm_tempstringsbuf_cursize;
738 int old_self, old_other;
740 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
743 old_self = prog->globals.server->self;
744 old_other = prog->globals.server->other;
745 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
747 VectorCopy(e1->fields.server->origin, org);
749 VM_SetTraceGlobals(trace);
751 prog->globals.server->time = sv.time;
752 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
754 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
755 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
756 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
759 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
761 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
762 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
763 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
764 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
765 prog->globals.server->trace_plane_dist = -trace->plane.dist;
766 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
767 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
769 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
771 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
773 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
775 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
778 prog->globals.server->self = old_self;
779 prog->globals.server->other = old_other;
780 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
782 return VectorCompare(e1->fields.server->origin, org);
790 Slide off of the impacting object
791 returns the blocked flags (1 = floor, 2 = step / wall)
794 #define STOP_EPSILON 0.1
795 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
800 backoff = -DotProduct (in, normal) * overbounce;
801 VectorMA(in, backoff, normal, out);
803 for (i = 0;i < 3;i++)
804 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
813 The basic solid body movement clip that slides along multiple planes
814 Returns the clipflags if the velocity was modified (hit something solid)
818 8 = teleported by touch method
819 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
822 static float SV_Gravity (prvm_edict_t *ent);
823 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
824 // LordHavoc: increased from 5 to 32
825 #define MAX_CLIP_PLANES 32
826 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
828 int blocked, bumpcount;
830 float d, time_left, gravity;
831 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
841 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
843 gravity = SV_Gravity(ent) * 0.5f;
844 ent->fields.server->velocity[2] -= gravity;
848 applygravity = false;
849 ent->fields.server->velocity[2] -= SV_Gravity(ent);
853 VectorCopy(ent->fields.server->velocity, original_velocity);
854 VectorCopy(ent->fields.server->velocity, primal_velocity);
857 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
859 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
862 VectorScale(ent->fields.server->velocity, time_left, push);
864 VectorAdd(ent->fields.server->origin, push, end);
866 if(!SV_PushEntity(&trace, ent, push, false, false))
868 // we got teleported by a touch function
869 // let's abort the move
875 //if (trace.fraction < 0.002)
880 VectorCopy(ent->fields.server->origin, start);
881 start[2] += 3;//0.03125;
882 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
883 end[2] += 3;//0.03125;
884 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
885 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)))
887 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
893 for (i = 0;i < numplanes;i++)
895 VectorCopy(ent->fields.server->origin, start);
896 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
897 VectorMA(start, 3, planes[i], start);
898 VectorMA(end, 3, planes[i], end);
899 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
900 if (trace.fraction < testtrace.fraction)
903 VectorCopy(start, ent->fields.server->origin);
908 // VectorAdd(ent->fields.server->origin, planes[j], start);
914 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);
915 if (trace.fraction < 1)
916 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
921 if (trace.bmodelstartsolid)
923 // LordHavoc: note: this code is what makes entities stick in place
924 // if embedded in world only (you can walk through other objects if
926 // entity is trapped in another solid
927 VectorClear(ent->fields.server->velocity);
932 if (trace.fraction == 1)
934 if (trace.plane.normal[2])
936 if (trace.plane.normal[2] > 0.7)
943 Con_Printf ("SV_FlyMove: !trace.ent");
944 trace.ent = prog->edicts;
947 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
948 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
955 // save the trace for player extrafriction
957 VectorCopy(trace.plane.normal, stepnormal);
959 if (trace.fraction >= 0.001)
961 // actually covered some distance
962 VectorCopy(ent->fields.server->velocity, original_velocity);
966 time_left *= 1 - trace.fraction;
968 // clipped to another plane
969 if (numplanes >= MAX_CLIP_PLANES)
971 // this shouldn't really happen
972 VectorClear(ent->fields.server->velocity);
978 for (i = 0;i < numplanes;i++)
979 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
983 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
988 VectorCopy(trace.plane.normal, planes[numplanes]);
991 if (sv_newflymove.integer)
992 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
995 // modify original_velocity so it parallels all of the clip planes
996 for (i = 0;i < numplanes;i++)
998 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
999 for (j = 0;j < numplanes;j++)
1004 if (DotProduct(new_velocity, planes[j]) < 0)
1014 // go along this plane
1015 VectorCopy(new_velocity, ent->fields.server->velocity);
1019 // go along the crease
1022 VectorClear(ent->fields.server->velocity);
1026 CrossProduct(planes[0], planes[1], dir);
1027 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1028 VectorNormalize(dir);
1029 d = DotProduct(dir, ent->fields.server->velocity);
1030 VectorScale(dir, d, ent->fields.server->velocity);
1034 // if current velocity is against the original velocity,
1035 // stop dead to avoid tiny occilations in sloping corners
1036 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1038 VectorClear(ent->fields.server->velocity);
1043 //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]);
1046 if ((blocked & 1) == 0 && bumpcount > 1)
1048 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1049 // flag ONGROUND if there's ground under it
1050 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1054 // LordHavoc: this came from QW and allows you to get out of water more easily
1055 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1056 VectorCopy(primal_velocity, ent->fields.server->velocity);
1057 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1058 ent->fields.server->velocity[2] -= gravity;
1068 static float SV_Gravity (prvm_edict_t *ent)
1073 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1074 if (val!=0 && val->_float)
1075 ent_gravity = val->_float;
1078 return ent_gravity * sv_gravity.value * sv.frametime;
1083 ===============================================================================
1087 ===============================================================================
1094 Does not change the entities velocity at all
1095 The trace struct is filled with the trace that has been done.
1096 Returns true if the push did not result in the entity being teleported by QC code.
1099 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1105 VectorAdd (ent->fields.server->origin, push, end);
1107 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1108 type = MOVE_MISSILE;
1109 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1110 type = MOVE_NOMONSTERS; // only clip against bmodels
1114 *trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1115 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1118 VectorCopy (trace->endpos, ent->fields.server->origin);
1121 if(!trace->startsolid)
1122 if(SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, type, ent, SV_GenericHitSuperContentsMask(ent)).startsolid)
1124 Con_Printf("something eeeeevil happened\n");
1128 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)));
1132 SV_LinkEdict (ent, dolink);
1133 return SV_Impact (ent, trace);
1136 SV_LinkEdict (ent, true);
1148 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1151 int pusherowner, pusherprog;
1154 float savesolid, movetime2, pushltime;
1155 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1157 int numcheckentities;
1158 static prvm_edict_t *checkentities[MAX_EDICTS];
1159 dp_model_t *pushermodel;
1160 trace_t trace, trace2;
1161 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1162 unsigned short moved_edicts[MAX_EDICTS];
1164 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])
1166 pusher->fields.server->ltime += movetime;
1170 switch ((int) pusher->fields.server->solid)
1172 // LordHavoc: valid pusher types
1175 case SOLID_SLIDEBOX:
1176 case SOLID_CORPSE: // LordHavoc: this would be weird...
1178 // LordHavoc: no collisions
1181 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1182 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1183 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1184 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1185 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1186 pusher->fields.server->ltime += movetime;
1187 SV_LinkEdict (pusher, false);
1190 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1193 index = (int) pusher->fields.server->modelindex;
1194 if (index < 1 || index >= MAX_MODELS)
1196 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1199 pushermodel = sv.models[index];
1200 pusherowner = pusher->fields.server->owner;
1201 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1203 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1205 movetime2 = movetime;
1206 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1207 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1208 if (moveangle[0] || moveangle[2])
1210 for (i = 0;i < 3;i++)
1214 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1215 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1219 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1220 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1224 else if (moveangle[1])
1226 for (i = 0;i < 3;i++)
1230 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1231 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1235 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1236 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1242 for (i = 0;i < 3;i++)
1246 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1247 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1251 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1252 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1257 VectorNegate (moveangle, a);
1258 AngleVectorsFLU (a, forward, left, up);
1260 VectorCopy (pusher->fields.server->origin, pushorig);
1261 VectorCopy (pusher->fields.server->angles, pushang);
1262 pushltime = pusher->fields.server->ltime;
1264 // move the pusher to its final position
1266 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1267 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1268 pusher->fields.server->ltime += movetime;
1269 SV_LinkEdict (pusher, false);
1272 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1273 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1274 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);
1275 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1277 savesolid = pusher->fields.server->solid;
1279 // see if any solid entities are inside the final position
1282 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1283 for (e = 0;e < numcheckentities;e++)
1285 prvm_edict_t *check = checkentities[e];
1286 if (check->fields.server->movetype == MOVETYPE_NONE
1287 || check->fields.server->movetype == MOVETYPE_PUSH
1288 || check->fields.server->movetype == MOVETYPE_FOLLOW
1289 || check->fields.server->movetype == MOVETYPE_NOCLIP
1290 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1293 if (check->fields.server->owner == pusherprog)
1296 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1299 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1301 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1302 check->priv.server->waterposition_forceupdate = true;
1304 checkcontents = SV_GenericHitSuperContentsMask(check);
1306 // if the entity is standing on the pusher, it will definitely be moved
1307 // if the entity is not standing on the pusher, but is in the pusher's
1308 // final position, move it
1309 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1311 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);
1312 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1313 if (!trace.startsolid)
1315 //Con_Printf("- not in solid\n");
1323 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1324 org2[0] = DotProduct (org, forward);
1325 org2[1] = DotProduct (org, left);
1326 org2[2] = DotProduct (org, up);
1327 VectorSubtract (org2, org, move);
1328 VectorAdd (move, move1, move);
1331 VectorCopy (move1, move);
1333 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1335 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1336 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1337 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1339 // try moving the contacted entity
1340 pusher->fields.server->solid = SOLID_NOT;
1341 if(!SV_PushEntity (&trace, check, move, true, true))
1343 // entity "check" got teleported
1344 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1345 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1346 continue; // pushed enough
1348 // FIXME: turn players specially
1349 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1350 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1351 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1353 // this trace.fraction < 1 check causes items to fall off of pushers
1354 // if they pass under or through a wall
1355 // the groundentity check causes items to fall off of ledges
1356 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1357 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1359 // if it is still inside the pusher, block
1360 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);
1361 if (trace.startsolid)
1363 // try moving the contacted entity a tiny bit further to account for precision errors
1365 pusher->fields.server->solid = SOLID_NOT;
1366 VectorScale(move, 1.1, move2);
1367 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1368 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1369 if(!SV_PushEntity (&trace2, check, move2, true, true))
1371 // entity "check" got teleported
1374 pusher->fields.server->solid = savesolid;
1375 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);
1376 if (trace.startsolid)
1378 // try moving the contacted entity a tiny bit less to account for precision errors
1379 pusher->fields.server->solid = SOLID_NOT;
1380 VectorScale(move, 0.9, move2);
1381 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1382 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1383 if(!SV_PushEntity (&trace2, check, move2, true, true))
1385 // entity "check" got teleported
1388 pusher->fields.server->solid = savesolid;
1389 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);
1390 if (trace.startsolid)
1392 // still inside pusher, so it's really blocked
1395 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1397 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1400 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1401 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1405 VectorCopy (pushorig, pusher->fields.server->origin);
1406 VectorCopy (pushang, pusher->fields.server->angles);
1407 pusher->fields.server->ltime = pushltime;
1408 SV_LinkEdict (pusher, false);
1410 // move back any entities we already moved
1411 for (i = 0;i < num_moved;i++)
1413 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1414 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1415 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1416 SV_LinkEdict (ed, false);
1419 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1420 if (pusher->fields.server->blocked)
1422 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1423 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1424 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1431 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1432 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1433 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1442 void SV_Physics_Pusher (prvm_edict_t *ent)
1444 float thinktime, oldltime, movetime;
1446 oldltime = ent->fields.server->ltime;
1448 thinktime = ent->fields.server->nextthink;
1449 if (thinktime < ent->fields.server->ltime + sv.frametime)
1451 movetime = thinktime - ent->fields.server->ltime;
1456 movetime = sv.frametime;
1459 // advances ent->fields.server->ltime if not blocked
1460 SV_PushMove (ent, movetime);
1462 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1464 ent->fields.server->nextthink = 0;
1465 prog->globals.server->time = sv.time;
1466 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1467 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1468 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1474 ===============================================================================
1478 ===============================================================================
1481 static float unstickoffsets[] =
1483 // poutting -/+z changes first as they are least weird
1498 typedef enum unstickresult_e
1506 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1510 // if not stuck in a bmodel, just return
1511 if (!SV_TestEntityPosition(ent, vec3_origin))
1512 return UNSTICK_GOOD;
1514 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1516 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1518 VectorCopy(unstickoffsets + i, offset);
1519 SV_LinkEdict (ent, true);
1520 return UNSTICK_UNSTUCK;
1524 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1525 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1527 for(i = 2; i <= maxunstick; ++i)
1529 VectorClear(offset);
1531 if (!SV_TestEntityPosition(ent, offset))
1533 SV_LinkEdict (ent, true);
1534 return UNSTICK_UNSTUCK;
1537 if (!SV_TestEntityPosition(ent, offset))
1539 SV_LinkEdict (ent, true);
1540 return UNSTICK_UNSTUCK;
1544 return UNSTICK_STUCK;
1547 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1550 switch(SV_UnstickEntityReturnOffset(ent, offset))
1554 case UNSTICK_UNSTUCK:
1555 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]);
1558 if (developer.integer >= 100)
1559 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1562 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1571 This is a big hack to try and fix the rare case of getting stuck in the world
1575 void SV_CheckStuck (prvm_edict_t *ent)
1579 switch(SV_UnstickEntityReturnOffset(ent, offset))
1582 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1584 case UNSTICK_UNSTUCK:
1585 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]);
1588 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1589 if (!SV_TestEntityPosition(ent, offset))
1591 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1592 SV_LinkEdict (ent, true);
1595 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1598 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1608 qboolean SV_CheckWater (prvm_edict_t *ent)
1611 int nNativeContents;
1614 point[0] = ent->fields.server->origin[0];
1615 point[1] = ent->fields.server->origin[1];
1616 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1618 // DRESK - Support for Entity Contents Transition Event
1619 // NOTE: Some logic needed to be slightly re-ordered
1620 // to not affect performance and allow for the feature.
1622 // Acquire Super Contents Prior to Resets
1623 cont = SV_PointSuperContents(point);
1624 // Acquire Native Contents Here
1625 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1627 // DRESK - Support for Entity Contents Transition Event
1628 if(ent->fields.server->watertype)
1629 // Entity did NOT Spawn; Check
1630 SV_CheckContentsTransition(ent, nNativeContents);
1633 ent->fields.server->waterlevel = 0;
1634 ent->fields.server->watertype = CONTENTS_EMPTY;
1635 cont = SV_PointSuperContents(point);
1636 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1638 ent->fields.server->watertype = nNativeContents;
1639 ent->fields.server->waterlevel = 1;
1640 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1641 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1643 ent->fields.server->waterlevel = 2;
1644 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1645 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1646 ent->fields.server->waterlevel = 3;
1650 return ent->fields.server->waterlevel > 1;
1659 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1662 vec3_t forward, into, side;
1664 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1665 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1667 // cut the tangential velocity
1668 i = DotProduct (stepnormal, ent->fields.server->velocity);
1669 VectorScale (stepnormal, i, into);
1670 VectorSubtract (ent->fields.server->velocity, into, side);
1671 ent->fields.server->velocity[0] = side[0] * (1 + d);
1672 ent->fields.server->velocity[1] = side[1] * (1 + d);
1678 =====================
1681 Player has come to a dead stop, possibly due to the problem with limited
1682 float precision at some angle joins in the BSP hull.
1684 Try fixing by pushing one pixel in each direction.
1686 This is a hack, but in the interest of good gameplay...
1687 ======================
1689 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1694 VectorCopy (ent->fields.server->origin, oldorg);
1697 for (i=0 ; i<8 ; i++)
1699 // try pushing a little in an axial direction
1702 case 0: dir[0] = 2; dir[1] = 0; break;
1703 case 1: dir[0] = 0; dir[1] = 2; break;
1704 case 2: dir[0] = -2; dir[1] = 0; break;
1705 case 3: dir[0] = 0; dir[1] = -2; break;
1706 case 4: dir[0] = 2; dir[1] = 2; break;
1707 case 5: dir[0] = -2; dir[1] = 2; break;
1708 case 6: dir[0] = 2; dir[1] = -2; break;
1709 case 7: dir[0] = -2; dir[1] = -2; break;
1712 SV_PushEntity (&trace, ent, dir, false, true);
1714 // retry the original move
1715 ent->fields.server->velocity[0] = oldvel[0];
1716 ent->fields.server->velocity[1] = oldvel[1];
1717 ent->fields.server->velocity[2] = 0;
1718 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1720 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1721 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1723 Con_DPrint("TryUnstick - success.\n");
1727 // go back to the original pos and try again
1728 VectorCopy (oldorg, ent->fields.server->origin);
1732 VectorClear (ent->fields.server->velocity);
1733 Con_DPrint("TryUnstick - failure.\n");
1739 =====================
1742 Only used by players
1743 ======================
1745 void SV_WalkMove (prvm_edict_t *ent)
1747 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1748 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1749 trace_t downtrace, trace;
1750 qboolean applygravity;
1752 // if frametime is 0 (due to client sending the same timestamp twice),
1754 if (sv.frametime <= 0)
1757 SV_CheckStuck (ent);
1759 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1761 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1763 SV_CheckVelocity(ent);
1765 // do a regular slide move unless it looks like you ran into a step
1766 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1768 VectorCopy (ent->fields.server->origin, start_origin);
1769 VectorCopy (ent->fields.server->velocity, start_velocity);
1771 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1773 // if the move did not hit the ground at any point, we're not on ground
1775 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1777 SV_CheckVelocity(ent);
1778 SV_LinkEdict (ent, true);
1780 if(clip & 8) // teleport
1783 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1786 if (sv_nostep.integer)
1789 VectorCopy(ent->fields.server->origin, originalmove_origin);
1790 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1791 originalmove_clip = clip;
1792 originalmove_flags = (int)ent->fields.server->flags;
1793 originalmove_groundentity = ent->fields.server->groundentity;
1795 // if move didn't block on a step, return
1798 // if move was not trying to move into the step, return
1799 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1802 if (ent->fields.server->movetype != MOVETYPE_FLY)
1804 // return if gibbed by a trigger
1805 if (ent->fields.server->movetype != MOVETYPE_WALK)
1808 // only step up while jumping if that is enabled
1809 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1810 if (!oldonground && ent->fields.server->waterlevel == 0)
1814 // try moving up and forward to go up a step
1815 // back to start pos
1816 VectorCopy (start_origin, ent->fields.server->origin);
1817 VectorCopy (start_velocity, ent->fields.server->velocity);
1820 VectorClear (upmove);
1821 upmove[2] = sv_stepheight.value;
1822 if(!SV_PushEntity(&trace, ent, upmove, false, true))
1824 // we got teleported when upstepping... must abort the move
1829 ent->fields.server->velocity[2] = 0;
1830 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1831 ent->fields.server->velocity[2] += start_velocity[2];
1834 // we got teleported when upstepping... must abort the move
1835 // note that z velocity handling may not be what QC expects here, but we cannot help it
1839 SV_CheckVelocity(ent);
1840 SV_LinkEdict (ent, true);
1842 // check for stuckness, possibly due to the limited precision of floats
1843 // in the clipping hulls
1845 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1846 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1848 //Con_Printf("wall\n");
1849 // stepping up didn't make any progress, revert to original move
1850 VectorCopy(originalmove_origin, ent->fields.server->origin);
1851 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1852 //clip = originalmove_clip;
1853 ent->fields.server->flags = originalmove_flags;
1854 ent->fields.server->groundentity = originalmove_groundentity;
1855 // now try to unstick if needed
1856 //clip = SV_TryUnstick (ent, oldvel);
1860 //Con_Printf("step - ");
1862 // extra friction based on view angle
1863 if (clip & 2 && sv_wallfriction.integer)
1864 SV_WallFriction (ent, stepnormal);
1866 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1867 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))
1871 VectorClear (downmove);
1872 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1873 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
1875 // we got teleported when downstepping... must abort the move
1879 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1881 // this has been disabled so that you can't jump when you are stepping
1882 // up while already jumping (also known as the Quake2 double jump bug)
1884 // LordHavoc: disabled this check so you can walk on monsters/players
1885 //if (ent->fields.server->solid == SOLID_BSP)
1887 //Con_Printf("onground\n");
1888 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1889 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1895 //Con_Printf("slope\n");
1896 // if the push down didn't end up on good ground, use the move without
1897 // the step up. This happens near wall / slope combinations, and can
1898 // cause the player to hop up higher on a slope too steep to climb
1899 VectorCopy(originalmove_origin, ent->fields.server->origin);
1900 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1901 //clip = originalmove_clip;
1902 ent->fields.server->flags = originalmove_flags;
1903 ent->fields.server->groundentity = originalmove_groundentity;
1906 SV_CheckVelocity(ent);
1907 SV_LinkEdict (ent, true);
1910 //============================================================================
1916 Entities that are "stuck" to another entity
1919 void SV_Physics_Follow (prvm_edict_t *ent)
1921 vec3_t vf, vr, vu, angles, v;
1925 if (!SV_RunThink (ent))
1928 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1929 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1930 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])
1932 // quick case for no rotation
1933 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1937 angles[0] = -ent->fields.server->punchangle[0];
1938 angles[1] = ent->fields.server->punchangle[1];
1939 angles[2] = ent->fields.server->punchangle[2];
1940 AngleVectors (angles, vf, vr, vu);
1941 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];
1942 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];
1943 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];
1944 angles[0] = -e->fields.server->angles[0];
1945 angles[1] = e->fields.server->angles[1];
1946 angles[2] = e->fields.server->angles[2];
1947 AngleVectors (angles, vf, vr, vu);
1948 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1949 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1950 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1952 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1953 SV_LinkEdict (ent, true);
1957 ==============================================================================
1961 ==============================================================================
1966 SV_CheckWaterTransition
1970 void SV_CheckWaterTransition (prvm_edict_t *ent)
1973 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1974 if (!ent->fields.server->watertype)
1976 // just spawned here
1977 ent->fields.server->watertype = cont;
1978 ent->fields.server->waterlevel = 1;
1982 // DRESK - Support for Entity Contents Transition Event
1983 // NOTE: Call here BEFORE updating the watertype below,
1984 // and suppress watersplash sound if a valid function
1985 // call was made to allow for custom "splash" sounds.
1986 if( !SV_CheckContentsTransition(ent, cont) )
1987 { // Contents Transition Function Invalid; Potentially Play Water Sound
1988 // check if the entity crossed into or out of water
1989 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1990 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1993 if (cont <= CONTENTS_WATER)
1995 ent->fields.server->watertype = cont;
1996 ent->fields.server->waterlevel = 1;
2000 ent->fields.server->watertype = CONTENTS_EMPTY;
2001 ent->fields.server->waterlevel = 0;
2009 Toss, bounce, and fly movement. When onground, do nothing.
2012 void SV_Physics_Toss (prvm_edict_t *ent)
2019 // if onground, return without moving
2020 if ((int)ent->fields.server->flags & FL_ONGROUND)
2022 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2024 // don't stick to ground if onground and moving upward
2025 ent->fields.server->flags -= FL_ONGROUND;
2027 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2029 // we can trust FL_ONGROUND if groundentity is world because it never moves
2032 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2034 // if ent was supported by a brush model on previous frame,
2035 // and groundentity is now freed, set groundentity to 0 (world)
2036 // which leaves it suspended in the air
2037 ent->fields.server->groundentity = 0;
2038 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2042 ent->priv.server->suspendedinairflag = false;
2044 SV_CheckVelocity (ent);
2047 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2048 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2051 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2053 movetime = sv.frametime;
2054 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2057 VectorScale (ent->fields.server->velocity, movetime, move);
2058 if(!SV_PushEntity (&trace, ent, move, true, true))
2059 return; // teleported
2060 if (ent->priv.server->free)
2062 if (trace.bmodelstartsolid)
2064 // try to unstick the entity
2065 SV_UnstickEntity(ent);
2066 if(!SV_PushEntity (&trace, ent, move, false, true))
2067 return; // teleported
2068 if (ent->priv.server->free)
2071 if (trace.fraction == 1)
2073 movetime *= 1 - min(1, trace.fraction);
2074 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2076 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2077 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2079 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2081 float d, ent_gravity;
2083 float bouncefactor = 0.5f;
2084 float bouncestop = 60.0f / 800.0f;
2086 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2087 if (val!=0 && val->_float)
2088 bouncefactor = val->_float;
2090 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2091 if (val!=0 && val->_float)
2092 bouncestop = val->_float;
2094 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2095 // LordHavoc: fixed grenades not bouncing when fired down a slope
2096 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2097 if (val!=0 && val->_float)
2098 ent_gravity = val->_float;
2101 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2103 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2104 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2106 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2107 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2108 VectorClear (ent->fields.server->velocity);
2109 VectorClear (ent->fields.server->avelocity);
2112 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2116 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2118 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2119 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2120 VectorClear (ent->fields.server->velocity);
2121 VectorClear (ent->fields.server->avelocity);
2124 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2129 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2130 if (trace.plane.normal[2] > 0.7)
2132 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2133 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2134 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2135 ent->priv.server->suspendedinairflag = true;
2136 VectorClear (ent->fields.server->velocity);
2137 VectorClear (ent->fields.server->avelocity);
2140 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2142 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2146 // check for in water
2147 SV_CheckWaterTransition (ent);
2151 ===============================================================================
2155 ===============================================================================
2162 Monsters freefall when they don't have a ground entity, otherwise
2163 all movement is done with discrete steps.
2165 This is also used for objects that have become still on the ground, but
2166 will fall if the floor is pulled out from under them.
2169 void SV_Physics_Step (prvm_edict_t *ent)
2171 int flags = (int)ent->fields.server->flags;
2174 // Backup Velocity in the event that movetypesteplandevent is called,
2175 // to provide a parameter with the entity's velocity at impact.
2176 prvm_eval_t *movetypesteplandevent;
2177 vec3_t backupVelocity;
2178 VectorCopy(ent->fields.server->velocity, backupVelocity);
2179 // don't fall at all if fly/swim
2180 if (!(flags & (FL_FLY | FL_SWIM)))
2182 if (flags & FL_ONGROUND)
2184 // freefall if onground and moving upward
2185 // freefall if not standing on a world surface (it may be a lift or trap door)
2186 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2188 ent->fields.server->flags -= FL_ONGROUND;
2189 SV_CheckVelocity(ent);
2190 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2191 SV_LinkEdict(ent, true);
2192 ent->priv.server->waterposition_forceupdate = true;
2197 // freefall if not onground
2198 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2200 SV_CheckVelocity(ent);
2201 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2202 SV_LinkEdict(ent, true);
2205 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2207 // DRESK - Check for Entity Land Event Function
2208 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2210 if(movetypesteplandevent->function)
2211 { // Valid Function; Execute
2212 // Prepare Parameters
2213 // Assign Velocity at Impact
2214 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2215 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2216 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2218 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2219 // Execute VM Function
2220 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2223 // Check for Engine Landing Sound
2224 if(sv_sound_land.string)
2225 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2227 ent->priv.server->waterposition_forceupdate = true;
2232 if (!SV_RunThink(ent))
2235 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2237 ent->priv.server->waterposition_forceupdate = false;
2238 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2239 SV_CheckWaterTransition(ent);
2243 //============================================================================
2245 static void SV_Physics_Entity (prvm_edict_t *ent)
2247 // don't run think/move on newly spawned projectiles as it messes up
2248 // movement interpolation and rocket trails, and is inconsistent with
2249 // respect to entities spawned in the same frame
2250 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2251 // but if it spawns a lower numbered ent, it doesn't - this never moves
2252 // ents in the first frame regardless)
2253 qboolean runmove = ent->priv.server->move;
2254 ent->priv.server->move = true;
2255 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2257 switch ((int) ent->fields.server->movetype)
2260 case MOVETYPE_FAKEPUSH:
2261 SV_Physics_Pusher (ent);
2264 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2265 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2268 case MOVETYPE_FOLLOW:
2269 SV_Physics_Follow (ent);
2271 case MOVETYPE_NOCLIP:
2272 if (SV_RunThink(ent))
2275 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2276 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2278 SV_LinkEdict(ent, false);
2281 SV_Physics_Step (ent);
2284 if (SV_RunThink (ent))
2288 case MOVETYPE_BOUNCE:
2289 case MOVETYPE_BOUNCEMISSILE:
2290 case MOVETYPE_FLYMISSILE:
2293 if (SV_RunThink (ent))
2294 SV_Physics_Toss (ent);
2297 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2302 void SV_Physics_ClientMove(void)
2305 ent = host_client->edict;
2307 // call player physics, this needs the proper frametime
2308 prog->globals.server->frametime = sv.frametime;
2311 // call standard client pre-think, with frametime = 0
2312 prog->globals.server->time = sv.time;
2313 prog->globals.server->frametime = 0;
2314 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2315 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2316 prog->globals.server->frametime = sv.frametime;
2318 // make sure the velocity is sane (not a NaN)
2319 SV_CheckVelocity(ent);
2320 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2321 // player_run/player_stand1 does not horribly malfunction if the
2322 // velocity becomes a number that is both == 0 and != 0
2323 // (sounds to me like NaN but to be absolutely safe...)
2324 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2325 VectorClear(ent->fields.server->velocity);
2327 // perform MOVETYPE_WALK behavior
2330 // call standard player post-think, with frametime = 0
2331 prog->globals.server->time = sv.time;
2332 prog->globals.server->frametime = 0;
2333 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2334 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2335 prog->globals.server->frametime = sv.frametime;
2337 if(ent->fields.server->fixangle)
2339 // angle fixing was requested by physics code...
2340 // so store the current angles for later use
2341 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2342 host_client->fixangle_angles_set = TRUE;
2344 // and clear fixangle for the next frame
2345 ent->fields.server->fixangle = 0;
2349 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2351 // don't do physics on disconnected clients, FrikBot relies on this
2352 if (!host_client->spawned)
2354 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2358 // don't run physics here if running asynchronously
2359 if (host_client->clmovement_inputtimeout <= 0)
2362 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2365 // make sure the velocity is sane (not a NaN)
2366 SV_CheckVelocity(ent);
2367 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2368 // player_run/player_stand1 does not horribly malfunction if the
2369 // velocity becomes a number that is both == 0 and != 0
2370 // (sounds to me like NaN but to be absolutely safe...)
2371 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2372 VectorClear(ent->fields.server->velocity);
2374 // call standard client pre-think
2375 prog->globals.server->time = sv.time;
2376 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2377 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2378 SV_CheckVelocity (ent);
2380 switch ((int) ent->fields.server->movetype)
2383 case MOVETYPE_FAKEPUSH:
2384 SV_Physics_Pusher (ent);
2387 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2388 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2391 case MOVETYPE_FOLLOW:
2392 SV_Physics_Follow (ent);
2394 case MOVETYPE_NOCLIP:
2397 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2398 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2401 SV_Physics_Step (ent);
2405 // don't run physics here if running asynchronously
2406 if (host_client->clmovement_inputtimeout <= 0)
2410 case MOVETYPE_BOUNCE:
2411 case MOVETYPE_BOUNCEMISSILE:
2412 case MOVETYPE_FLYMISSILE:
2415 SV_Physics_Toss (ent);
2422 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2426 // decrement the countdown variable used to decide when to go back to
2427 // synchronous physics
2428 if (host_client->clmovement_inputtimeout > sv.frametime)
2429 host_client->clmovement_inputtimeout -= sv.frametime;
2431 host_client->clmovement_inputtimeout = 0;
2433 SV_CheckVelocity (ent);
2435 SV_LinkEdict (ent, true);
2437 SV_CheckVelocity (ent);
2439 // call standard player post-think
2440 prog->globals.server->time = sv.time;
2441 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2442 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2444 if(ent->fields.server->fixangle)
2446 // angle fixing was requested by physics code...
2447 // so store the current angles for later use
2448 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2449 host_client->fixangle_angles_set = TRUE;
2451 // and clear fixangle for the next frame
2452 ent->fields.server->fixangle = 0;
2462 void SV_Physics (void)
2467 // let the progs know that a new frame has started
2468 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2469 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2470 prog->globals.server->time = sv.time;
2471 prog->globals.server->frametime = sv.frametime;
2472 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2475 // treat each object in turn
2478 // if force_retouch, relink all the entities
2479 if (prog->globals.server->force_retouch > 0)
2480 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2481 if (!ent->priv.server->free)
2482 SV_LinkEdict (ent, true); // force retouch even for stationary
2484 // run physics on the client entities
2485 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2486 if (!ent->priv.server->free)
2487 SV_Physics_ClientEntity(ent);
2489 // run physics on all the non-client entities
2490 if (!sv_freezenonclients.integer)
2492 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2493 if (!ent->priv.server->free)
2494 SV_Physics_Entity(ent);
2495 // make a second pass to see if any ents spawned this frame and make
2496 // sure they run their move/think
2497 if (sv_gameplayfix_delayprojectiles.integer < 0)
2498 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2499 if (!ent->priv.server->move && !ent->priv.server->free)
2500 SV_Physics_Entity(ent);
2503 if (prog->globals.server->force_retouch > 0)
2504 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2506 // LordHavoc: endframe support
2507 if (prog->funcoffsets.EndFrame)
2509 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2510 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2511 prog->globals.server->time = sv.time;
2512 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2515 // decrement prog->num_edicts if the highest number entities died
2516 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2518 if (!sv_freezenonclients.integer)
2519 sv.time += sv.frametime;