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 #define MAX_CLIP_PLANES 5
825 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
827 int blocked, bumpcount;
829 float d, time_left, gravity;
830 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
840 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
842 gravity = SV_Gravity(ent) * 0.5f;
843 ent->fields.server->velocity[2] -= gravity;
847 applygravity = false;
848 ent->fields.server->velocity[2] -= SV_Gravity(ent);
852 VectorCopy(ent->fields.server->velocity, original_velocity);
853 VectorCopy(ent->fields.server->velocity, primal_velocity);
856 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
858 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
861 VectorScale(ent->fields.server->velocity, time_left, push);
863 VectorAdd(ent->fields.server->origin, push, end);
865 if(!SV_PushEntity(&trace, ent, push, false, false))
867 // we got teleported by a touch function
868 // let's abort the move
874 //if (trace.fraction < 0.002)
879 VectorCopy(ent->fields.server->origin, start);
880 start[2] += 3;//0.03125;
881 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
882 end[2] += 3;//0.03125;
883 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
884 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)))
886 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
892 for (i = 0;i < numplanes;i++)
894 VectorCopy(ent->fields.server->origin, start);
895 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
896 VectorMA(start, 3, planes[i], start);
897 VectorMA(end, 3, planes[i], end);
898 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
899 if (trace.fraction < testtrace.fraction)
902 VectorCopy(start, ent->fields.server->origin);
907 // VectorAdd(ent->fields.server->origin, planes[j], start);
913 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);
914 if (trace.fraction < 1)
915 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
920 if (trace.bmodelstartsolid)
922 // LordHavoc: note: this code is what makes entities stick in place
923 // if embedded in world only (you can walk through other objects if
925 // entity is trapped in another solid
926 VectorClear(ent->fields.server->velocity);
931 if (trace.fraction == 1)
933 if (trace.plane.normal[2])
935 if (trace.plane.normal[2] > 0.7)
942 Con_Printf ("SV_FlyMove: !trace.ent");
943 trace.ent = prog->edicts;
946 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
947 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
954 // save the trace for player extrafriction
956 VectorCopy(trace.plane.normal, stepnormal);
958 if (trace.fraction >= 0.001)
960 // actually covered some distance
961 VectorCopy(ent->fields.server->velocity, original_velocity);
965 time_left *= 1 - trace.fraction;
967 // clipped to another plane
968 if (numplanes >= MAX_CLIP_PLANES)
970 // this shouldn't really happen
971 VectorClear(ent->fields.server->velocity);
977 for (i = 0;i < numplanes;i++)
978 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
982 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
987 VectorCopy(trace.plane.normal, planes[numplanes]);
990 if (sv_newflymove.integer)
991 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
994 // modify original_velocity so it parallels all of the clip planes
995 for (i = 0;i < numplanes;i++)
997 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
998 for (j = 0;j < numplanes;j++)
1003 if (DotProduct(new_velocity, planes[j]) < 0)
1013 // go along this plane
1014 VectorCopy(new_velocity, ent->fields.server->velocity);
1018 // go along the crease
1021 VectorClear(ent->fields.server->velocity);
1025 CrossProduct(planes[0], planes[1], dir);
1026 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1027 VectorNormalize(dir);
1028 d = DotProduct(dir, ent->fields.server->velocity);
1029 VectorScale(dir, d, ent->fields.server->velocity);
1033 // if current velocity is against the original velocity,
1034 // stop dead to avoid tiny occilations in sloping corners
1035 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1037 VectorClear(ent->fields.server->velocity);
1042 //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]);
1045 if ((blocked & 1) == 0 && bumpcount > 1)
1047 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1048 // flag ONGROUND if there's ground under it
1049 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1053 // LordHavoc: this came from QW and allows you to get out of water more easily
1054 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1055 VectorCopy(primal_velocity, ent->fields.server->velocity);
1056 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1057 ent->fields.server->velocity[2] -= gravity;
1067 static float SV_Gravity (prvm_edict_t *ent)
1072 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1073 if (val!=0 && val->_float)
1074 ent_gravity = val->_float;
1077 return ent_gravity * sv_gravity.value * sv.frametime;
1082 ===============================================================================
1086 ===============================================================================
1093 Does not change the entities velocity at all
1094 The trace struct is filled with the trace that has been done.
1095 Returns true if the push did not result in the entity being teleported by QC code.
1098 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1104 VectorAdd (ent->fields.server->origin, push, end);
1106 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1107 type = MOVE_MISSILE;
1108 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1109 type = MOVE_NOMONSTERS; // only clip against bmodels
1113 *trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1114 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1117 VectorCopy (trace->endpos, ent->fields.server->origin);
1120 if(!trace->startsolid)
1121 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)
1123 Con_Printf("something eeeeevil happened\n");
1127 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)));
1131 SV_LinkEdict (ent, dolink);
1132 return SV_Impact (ent, trace);
1135 SV_LinkEdict (ent, true);
1147 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1150 int pusherowner, pusherprog;
1153 float savesolid, movetime2, pushltime;
1154 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1156 int numcheckentities;
1157 static prvm_edict_t *checkentities[MAX_EDICTS];
1158 dp_model_t *pushermodel;
1159 trace_t trace, trace2;
1160 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1161 unsigned short moved_edicts[MAX_EDICTS];
1163 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])
1165 pusher->fields.server->ltime += movetime;
1169 switch ((int) pusher->fields.server->solid)
1171 // LordHavoc: valid pusher types
1174 case SOLID_SLIDEBOX:
1175 case SOLID_CORPSE: // LordHavoc: this would be weird...
1177 // LordHavoc: no collisions
1180 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1181 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1182 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1183 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1184 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1185 pusher->fields.server->ltime += movetime;
1186 SV_LinkEdict (pusher, false);
1189 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1192 index = (int) pusher->fields.server->modelindex;
1193 if (index < 1 || index >= MAX_MODELS)
1195 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1198 pushermodel = sv.models[index];
1199 pusherowner = pusher->fields.server->owner;
1200 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1202 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1204 movetime2 = movetime;
1205 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1206 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1207 if (moveangle[0] || moveangle[2])
1209 for (i = 0;i < 3;i++)
1213 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1214 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1218 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1219 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1223 else if (moveangle[1])
1225 for (i = 0;i < 3;i++)
1229 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1230 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1234 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1235 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1241 for (i = 0;i < 3;i++)
1245 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1246 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1250 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1251 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1256 VectorNegate (moveangle, a);
1257 AngleVectorsFLU (a, forward, left, up);
1259 VectorCopy (pusher->fields.server->origin, pushorig);
1260 VectorCopy (pusher->fields.server->angles, pushang);
1261 pushltime = pusher->fields.server->ltime;
1263 // move the pusher to its final position
1265 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1266 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1267 pusher->fields.server->ltime += movetime;
1268 SV_LinkEdict (pusher, false);
1271 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1272 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1273 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);
1274 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1276 savesolid = pusher->fields.server->solid;
1278 // see if any solid entities are inside the final position
1281 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1282 for (e = 0;e < numcheckentities;e++)
1284 prvm_edict_t *check = checkentities[e];
1285 if (check->fields.server->movetype == MOVETYPE_NONE
1286 || check->fields.server->movetype == MOVETYPE_PUSH
1287 || check->fields.server->movetype == MOVETYPE_FOLLOW
1288 || check->fields.server->movetype == MOVETYPE_NOCLIP
1289 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1292 if (check->fields.server->owner == pusherprog)
1295 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1298 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1300 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1301 check->priv.server->waterposition_forceupdate = true;
1303 checkcontents = SV_GenericHitSuperContentsMask(check);
1305 // if the entity is standing on the pusher, it will definitely be moved
1306 // if the entity is not standing on the pusher, but is in the pusher's
1307 // final position, move it
1308 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1310 Collision_ClipToGenericEntity(&trace, pushermodel, (int) pusher->fields.server->frame, pusher->fields.server->mins, pusher->fields.server->maxs, SUPERCONTENTS_BODY, &pusherfinalmatrix, &pusherfinalimatrix, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, checkcontents);
1311 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1312 if (!trace.startsolid)
1314 //Con_Printf("- not in solid\n");
1322 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1323 org2[0] = DotProduct (org, forward);
1324 org2[1] = DotProduct (org, left);
1325 org2[2] = DotProduct (org, up);
1326 VectorSubtract (org2, org, move);
1327 VectorAdd (move, move1, move);
1330 VectorCopy (move1, move);
1332 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1334 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1335 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1336 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1338 // try moving the contacted entity
1339 pusher->fields.server->solid = SOLID_NOT;
1340 if(!SV_PushEntity (&trace, check, move, true, true))
1342 // entity "check" got teleported
1343 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1344 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1345 continue; // pushed enough
1347 // FIXME: turn players specially
1348 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1349 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1350 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1352 // this trace.fraction < 1 check causes items to fall off of pushers
1353 // if they pass under or through a wall
1354 // the groundentity check causes items to fall off of ledges
1355 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1356 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1358 // if it is still inside the pusher, block
1359 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);
1360 if (trace.startsolid)
1362 // try moving the contacted entity a tiny bit further to account for precision errors
1364 pusher->fields.server->solid = SOLID_NOT;
1365 VectorScale(move, 1.1, move2);
1366 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1367 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1368 if(!SV_PushEntity (&trace2, check, move2, true, true))
1370 // entity "check" got teleported
1373 pusher->fields.server->solid = savesolid;
1374 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);
1375 if (trace.startsolid)
1377 // try moving the contacted entity a tiny bit less to account for precision errors
1378 pusher->fields.server->solid = SOLID_NOT;
1379 VectorScale(move, 0.9, move2);
1380 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1381 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1382 if(!SV_PushEntity (&trace2, check, move2, true, true))
1384 // entity "check" got teleported
1387 pusher->fields.server->solid = savesolid;
1388 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);
1389 if (trace.startsolid)
1391 // still inside pusher, so it's really blocked
1394 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1396 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1399 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1400 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1404 VectorCopy (pushorig, pusher->fields.server->origin);
1405 VectorCopy (pushang, pusher->fields.server->angles);
1406 pusher->fields.server->ltime = pushltime;
1407 SV_LinkEdict (pusher, false);
1409 // move back any entities we already moved
1410 for (i = 0;i < num_moved;i++)
1412 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1413 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1414 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1415 SV_LinkEdict (ed, false);
1418 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1419 if (pusher->fields.server->blocked)
1421 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1422 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1423 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1430 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1431 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1432 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1441 void SV_Physics_Pusher (prvm_edict_t *ent)
1443 float thinktime, oldltime, movetime;
1445 oldltime = ent->fields.server->ltime;
1447 thinktime = ent->fields.server->nextthink;
1448 if (thinktime < ent->fields.server->ltime + sv.frametime)
1450 movetime = thinktime - ent->fields.server->ltime;
1455 movetime = sv.frametime;
1458 // advances ent->fields.server->ltime if not blocked
1459 SV_PushMove (ent, movetime);
1461 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1463 ent->fields.server->nextthink = 0;
1464 prog->globals.server->time = sv.time;
1465 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1466 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1467 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1473 ===============================================================================
1477 ===============================================================================
1480 static float unstickoffsets[] =
1482 // poutting -/+z changes first as they are least weird
1497 typedef enum unstickresult_e
1505 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1509 // if not stuck in a bmodel, just return
1510 if (!SV_TestEntityPosition(ent, vec3_origin))
1511 return UNSTICK_GOOD;
1513 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1515 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1517 VectorCopy(unstickoffsets + i, offset);
1518 SV_LinkEdict (ent, true);
1519 return UNSTICK_UNSTUCK;
1523 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1524 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1526 for(i = 2; i <= maxunstick; ++i)
1528 VectorClear(offset);
1530 if (!SV_TestEntityPosition(ent, offset))
1532 SV_LinkEdict (ent, true);
1533 return UNSTICK_UNSTUCK;
1536 if (!SV_TestEntityPosition(ent, offset))
1538 SV_LinkEdict (ent, true);
1539 return UNSTICK_UNSTUCK;
1543 return UNSTICK_STUCK;
1546 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1549 switch(SV_UnstickEntityReturnOffset(ent, offset))
1553 case UNSTICK_UNSTUCK:
1554 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]);
1557 if (developer.integer >= 100)
1558 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1561 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1570 This is a big hack to try and fix the rare case of getting stuck in the world
1574 void SV_CheckStuck (prvm_edict_t *ent)
1578 switch(SV_UnstickEntityReturnOffset(ent, offset))
1581 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1583 case UNSTICK_UNSTUCK:
1584 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]);
1587 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1588 if (!SV_TestEntityPosition(ent, offset))
1590 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1591 SV_LinkEdict (ent, true);
1594 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1597 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1607 qboolean SV_CheckWater (prvm_edict_t *ent)
1610 int nNativeContents;
1613 point[0] = ent->fields.server->origin[0];
1614 point[1] = ent->fields.server->origin[1];
1615 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1617 // DRESK - Support for Entity Contents Transition Event
1618 // NOTE: Some logic needed to be slightly re-ordered
1619 // to not affect performance and allow for the feature.
1621 // Acquire Super Contents Prior to Resets
1622 cont = SV_PointSuperContents(point);
1623 // Acquire Native Contents Here
1624 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1626 // DRESK - Support for Entity Contents Transition Event
1627 if(ent->fields.server->watertype)
1628 // Entity did NOT Spawn; Check
1629 SV_CheckContentsTransition(ent, nNativeContents);
1632 ent->fields.server->waterlevel = 0;
1633 ent->fields.server->watertype = CONTENTS_EMPTY;
1634 cont = SV_PointSuperContents(point);
1635 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1637 ent->fields.server->watertype = nNativeContents;
1638 ent->fields.server->waterlevel = 1;
1639 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1640 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1642 ent->fields.server->waterlevel = 2;
1643 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1644 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1645 ent->fields.server->waterlevel = 3;
1649 return ent->fields.server->waterlevel > 1;
1658 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1661 vec3_t forward, into, side;
1663 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1664 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1666 // cut the tangential velocity
1667 i = DotProduct (stepnormal, ent->fields.server->velocity);
1668 VectorScale (stepnormal, i, into);
1669 VectorSubtract (ent->fields.server->velocity, into, side);
1670 ent->fields.server->velocity[0] = side[0] * (1 + d);
1671 ent->fields.server->velocity[1] = side[1] * (1 + d);
1677 =====================
1680 Player has come to a dead stop, possibly due to the problem with limited
1681 float precision at some angle joins in the BSP hull.
1683 Try fixing by pushing one pixel in each direction.
1685 This is a hack, but in the interest of good gameplay...
1686 ======================
1688 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1693 VectorCopy (ent->fields.server->origin, oldorg);
1696 for (i=0 ; i<8 ; i++)
1698 // try pushing a little in an axial direction
1701 case 0: dir[0] = 2; dir[1] = 0; break;
1702 case 1: dir[0] = 0; dir[1] = 2; break;
1703 case 2: dir[0] = -2; dir[1] = 0; break;
1704 case 3: dir[0] = 0; dir[1] = -2; break;
1705 case 4: dir[0] = 2; dir[1] = 2; break;
1706 case 5: dir[0] = -2; dir[1] = 2; break;
1707 case 6: dir[0] = 2; dir[1] = -2; break;
1708 case 7: dir[0] = -2; dir[1] = -2; break;
1711 SV_PushEntity (&trace, ent, dir, false, true);
1713 // retry the original move
1714 ent->fields.server->velocity[0] = oldvel[0];
1715 ent->fields.server->velocity[1] = oldvel[1];
1716 ent->fields.server->velocity[2] = 0;
1717 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1719 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1720 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1722 Con_DPrint("TryUnstick - success.\n");
1726 // go back to the original pos and try again
1727 VectorCopy (oldorg, ent->fields.server->origin);
1731 VectorClear (ent->fields.server->velocity);
1732 Con_DPrint("TryUnstick - failure.\n");
1738 =====================
1741 Only used by players
1742 ======================
1744 void SV_WalkMove (prvm_edict_t *ent)
1746 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1747 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1748 trace_t downtrace, trace;
1749 qboolean applygravity;
1751 // if frametime is 0 (due to client sending the same timestamp twice),
1753 if (sv.frametime <= 0)
1756 SV_CheckStuck (ent);
1758 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1760 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1762 SV_CheckVelocity(ent);
1764 // do a regular slide move unless it looks like you ran into a step
1765 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1767 VectorCopy (ent->fields.server->origin, start_origin);
1768 VectorCopy (ent->fields.server->velocity, start_velocity);
1770 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1772 // if the move did not hit the ground at any point, we're not on ground
1774 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1776 SV_CheckVelocity(ent);
1777 SV_LinkEdict (ent, true);
1779 if(clip & 8) // teleport
1782 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1785 if (sv_nostep.integer)
1788 VectorCopy(ent->fields.server->origin, originalmove_origin);
1789 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1790 originalmove_clip = clip;
1791 originalmove_flags = (int)ent->fields.server->flags;
1792 originalmove_groundentity = ent->fields.server->groundentity;
1794 // if move didn't block on a step, return
1797 // if move was not trying to move into the step, return
1798 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1801 if (ent->fields.server->movetype != MOVETYPE_FLY)
1803 // return if gibbed by a trigger
1804 if (ent->fields.server->movetype != MOVETYPE_WALK)
1807 // only step up while jumping if that is enabled
1808 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1809 if (!oldonground && ent->fields.server->waterlevel == 0)
1813 // try moving up and forward to go up a step
1814 // back to start pos
1815 VectorCopy (start_origin, ent->fields.server->origin);
1816 VectorCopy (start_velocity, ent->fields.server->velocity);
1819 VectorClear (upmove);
1820 upmove[2] = sv_stepheight.value;
1821 if(!SV_PushEntity(&trace, ent, upmove, false, true))
1823 // we got teleported when upstepping... must abort the move
1828 ent->fields.server->velocity[2] = 0;
1829 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1830 ent->fields.server->velocity[2] += start_velocity[2];
1833 // we got teleported when upstepping... must abort the move
1834 // note that z velocity handling may not be what QC expects here, but we cannot help it
1838 SV_CheckVelocity(ent);
1839 SV_LinkEdict (ent, true);
1841 // check for stuckness, possibly due to the limited precision of floats
1842 // in the clipping hulls
1844 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1845 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1847 //Con_Printf("wall\n");
1848 // stepping up didn't make any progress, revert to original move
1849 VectorCopy(originalmove_origin, ent->fields.server->origin);
1850 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1851 //clip = originalmove_clip;
1852 ent->fields.server->flags = originalmove_flags;
1853 ent->fields.server->groundentity = originalmove_groundentity;
1854 // now try to unstick if needed
1855 //clip = SV_TryUnstick (ent, oldvel);
1859 //Con_Printf("step - ");
1861 // extra friction based on view angle
1862 if (clip & 2 && sv_wallfriction.integer)
1863 SV_WallFriction (ent, stepnormal);
1865 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1866 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))
1870 VectorClear (downmove);
1871 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1872 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
1874 // we got teleported when downstepping... must abort the move
1878 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1880 // this has been disabled so that you can't jump when you are stepping
1881 // up while already jumping (also known as the Quake2 double jump bug)
1883 // LordHavoc: disabled this check so you can walk on monsters/players
1884 //if (ent->fields.server->solid == SOLID_BSP)
1886 //Con_Printf("onground\n");
1887 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1888 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1894 //Con_Printf("slope\n");
1895 // if the push down didn't end up on good ground, use the move without
1896 // the step up. This happens near wall / slope combinations, and can
1897 // cause the player to hop up higher on a slope too steep to climb
1898 VectorCopy(originalmove_origin, ent->fields.server->origin);
1899 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1900 //clip = originalmove_clip;
1901 ent->fields.server->flags = originalmove_flags;
1902 ent->fields.server->groundentity = originalmove_groundentity;
1905 SV_CheckVelocity(ent);
1906 SV_LinkEdict (ent, true);
1909 //============================================================================
1915 Entities that are "stuck" to another entity
1918 void SV_Physics_Follow (prvm_edict_t *ent)
1920 vec3_t vf, vr, vu, angles, v;
1924 if (!SV_RunThink (ent))
1927 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1928 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1929 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])
1931 // quick case for no rotation
1932 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1936 angles[0] = -ent->fields.server->punchangle[0];
1937 angles[1] = ent->fields.server->punchangle[1];
1938 angles[2] = ent->fields.server->punchangle[2];
1939 AngleVectors (angles, vf, vr, vu);
1940 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];
1941 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];
1942 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];
1943 angles[0] = -e->fields.server->angles[0];
1944 angles[1] = e->fields.server->angles[1];
1945 angles[2] = e->fields.server->angles[2];
1946 AngleVectors (angles, vf, vr, vu);
1947 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1948 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1949 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1951 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1952 SV_LinkEdict (ent, true);
1956 ==============================================================================
1960 ==============================================================================
1965 SV_CheckWaterTransition
1969 void SV_CheckWaterTransition (prvm_edict_t *ent)
1972 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1973 if (!ent->fields.server->watertype)
1975 // just spawned here
1976 ent->fields.server->watertype = cont;
1977 ent->fields.server->waterlevel = 1;
1981 // DRESK - Support for Entity Contents Transition Event
1982 // NOTE: Call here BEFORE updating the watertype below,
1983 // and suppress watersplash sound if a valid function
1984 // call was made to allow for custom "splash" sounds.
1985 if( !SV_CheckContentsTransition(ent, cont) )
1986 { // Contents Transition Function Invalid; Potentially Play Water Sound
1987 // check if the entity crossed into or out of water
1988 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1989 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1992 if (cont <= CONTENTS_WATER)
1994 ent->fields.server->watertype = cont;
1995 ent->fields.server->waterlevel = 1;
1999 ent->fields.server->watertype = CONTENTS_EMPTY;
2000 ent->fields.server->waterlevel = 0;
2008 Toss, bounce, and fly movement. When onground, do nothing.
2011 void SV_Physics_Toss (prvm_edict_t *ent)
2018 // if onground, return without moving
2019 if ((int)ent->fields.server->flags & FL_ONGROUND)
2021 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
2023 // don't stick to ground if onground and moving upward
2024 ent->fields.server->flags -= FL_ONGROUND;
2026 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2028 // we can trust FL_ONGROUND if groundentity is world because it never moves
2031 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2033 // if ent was supported by a brush model on previous frame,
2034 // and groundentity is now freed, set groundentity to 0 (world)
2035 // which leaves it suspended in the air
2036 ent->fields.server->groundentity = 0;
2037 if (sv_gameplayfix_noairborncorpse_allowsuspendeditems.integer)
2041 ent->priv.server->suspendedinairflag = false;
2043 SV_CheckVelocity (ent);
2046 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2047 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2050 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2052 movetime = sv.frametime;
2053 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2056 VectorScale (ent->fields.server->velocity, movetime, move);
2057 if(!SV_PushEntity (&trace, ent, move, true, true))
2058 return; // teleported
2059 if (ent->priv.server->free)
2061 if (trace.bmodelstartsolid)
2063 // try to unstick the entity
2064 SV_UnstickEntity(ent);
2065 if(!SV_PushEntity (&trace, ent, move, false, true))
2066 return; // teleported
2067 if (ent->priv.server->free)
2070 if (trace.fraction == 1)
2072 movetime *= 1 - min(1, trace.fraction);
2073 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2075 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2076 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2078 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2080 float d, ent_gravity;
2082 float bouncefactor = 0.5f;
2083 float bouncestop = 60.0f / 800.0f;
2085 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2086 if (val!=0 && val->_float)
2087 bouncefactor = val->_float;
2089 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2090 if (val!=0 && val->_float)
2091 bouncestop = val->_float;
2093 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2094 // LordHavoc: fixed grenades not bouncing when fired down a slope
2095 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2096 if (val!=0 && val->_float)
2097 ent_gravity = val->_float;
2100 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2102 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2103 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2105 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2106 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2107 VectorClear (ent->fields.server->velocity);
2108 VectorClear (ent->fields.server->avelocity);
2111 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2115 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2117 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2118 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2119 VectorClear (ent->fields.server->velocity);
2120 VectorClear (ent->fields.server->avelocity);
2123 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2128 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2129 if (trace.plane.normal[2] > 0.7)
2131 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2132 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2133 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2134 ent->priv.server->suspendedinairflag = true;
2135 VectorClear (ent->fields.server->velocity);
2136 VectorClear (ent->fields.server->avelocity);
2139 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2141 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2145 // check for in water
2146 SV_CheckWaterTransition (ent);
2150 ===============================================================================
2154 ===============================================================================
2161 Monsters freefall when they don't have a ground entity, otherwise
2162 all movement is done with discrete steps.
2164 This is also used for objects that have become still on the ground, but
2165 will fall if the floor is pulled out from under them.
2168 void SV_Physics_Step (prvm_edict_t *ent)
2170 int flags = (int)ent->fields.server->flags;
2173 // Backup Velocity in the event that movetypesteplandevent is called,
2174 // to provide a parameter with the entity's velocity at impact.
2175 prvm_eval_t *movetypesteplandevent;
2176 vec3_t backupVelocity;
2177 VectorCopy(ent->fields.server->velocity, backupVelocity);
2178 // don't fall at all if fly/swim
2179 if (!(flags & (FL_FLY | FL_SWIM)))
2181 if (flags & FL_ONGROUND)
2183 // freefall if onground and moving upward
2184 // freefall if not standing on a world surface (it may be a lift or trap door)
2185 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2187 ent->fields.server->flags -= FL_ONGROUND;
2188 SV_CheckVelocity(ent);
2189 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2190 SV_LinkEdict(ent, true);
2191 ent->priv.server->waterposition_forceupdate = true;
2196 // freefall if not onground
2197 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2199 SV_CheckVelocity(ent);
2200 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2201 SV_LinkEdict(ent, true);
2204 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2206 // DRESK - Check for Entity Land Event Function
2207 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2209 if(movetypesteplandevent->function)
2210 { // Valid Function; Execute
2211 // Prepare Parameters
2212 // Assign Velocity at Impact
2213 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2214 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2215 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2217 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2218 // Execute VM Function
2219 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2222 // Check for Engine Landing Sound
2223 if(sv_sound_land.string)
2224 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2226 ent->priv.server->waterposition_forceupdate = true;
2231 if (!SV_RunThink(ent))
2234 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2236 ent->priv.server->waterposition_forceupdate = false;
2237 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2238 SV_CheckWaterTransition(ent);
2242 //============================================================================
2244 static void SV_Physics_Entity (prvm_edict_t *ent)
2246 // don't run think/move on newly spawned projectiles as it messes up
2247 // movement interpolation and rocket trails, and is inconsistent with
2248 // respect to entities spawned in the same frame
2249 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2250 // but if it spawns a lower numbered ent, it doesn't - this never moves
2251 // ents in the first frame regardless)
2252 qboolean runmove = ent->priv.server->move;
2253 ent->priv.server->move = true;
2254 if (!runmove && sv_gameplayfix_delayprojectiles.integer > 0)
2256 switch ((int) ent->fields.server->movetype)
2259 case MOVETYPE_FAKEPUSH:
2260 SV_Physics_Pusher (ent);
2263 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2264 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2267 case MOVETYPE_FOLLOW:
2268 SV_Physics_Follow (ent);
2270 case MOVETYPE_NOCLIP:
2271 if (SV_RunThink(ent))
2274 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2275 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2277 SV_LinkEdict(ent, false);
2280 SV_Physics_Step (ent);
2283 if (SV_RunThink (ent))
2287 case MOVETYPE_BOUNCE:
2288 case MOVETYPE_BOUNCEMISSILE:
2289 case MOVETYPE_FLYMISSILE:
2292 if (SV_RunThink (ent))
2293 SV_Physics_Toss (ent);
2296 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2301 void SV_Physics_ClientMove(void)
2304 ent = host_client->edict;
2306 // call player physics, this needs the proper frametime
2307 prog->globals.server->frametime = sv.frametime;
2310 // call standard client pre-think, with frametime = 0
2311 prog->globals.server->time = sv.time;
2312 prog->globals.server->frametime = 0;
2313 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2314 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2315 prog->globals.server->frametime = sv.frametime;
2317 // make sure the velocity is sane (not a NaN)
2318 SV_CheckVelocity(ent);
2319 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2320 // player_run/player_stand1 does not horribly malfunction if the
2321 // velocity becomes a number that is both == 0 and != 0
2322 // (sounds to me like NaN but to be absolutely safe...)
2323 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2324 VectorClear(ent->fields.server->velocity);
2326 // perform MOVETYPE_WALK behavior
2329 // call standard player post-think, with frametime = 0
2330 prog->globals.server->time = sv.time;
2331 prog->globals.server->frametime = 0;
2332 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2333 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2334 prog->globals.server->frametime = sv.frametime;
2336 if(ent->fields.server->fixangle)
2338 // angle fixing was requested by physics code...
2339 // so store the current angles for later use
2340 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2341 host_client->fixangle_angles_set = TRUE;
2343 // and clear fixangle for the next frame
2344 ent->fields.server->fixangle = 0;
2348 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2350 // don't do physics on disconnected clients, FrikBot relies on this
2351 if (!host_client->spawned)
2353 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2357 // don't run physics here if running asynchronously
2358 if (host_client->clmovement_inputtimeout <= 0)
2361 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2364 // make sure the velocity is sane (not a NaN)
2365 SV_CheckVelocity(ent);
2366 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2367 // player_run/player_stand1 does not horribly malfunction if the
2368 // velocity becomes a number that is both == 0 and != 0
2369 // (sounds to me like NaN but to be absolutely safe...)
2370 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2371 VectorClear(ent->fields.server->velocity);
2373 // call standard client pre-think
2374 prog->globals.server->time = sv.time;
2375 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2376 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2377 SV_CheckVelocity (ent);
2379 switch ((int) ent->fields.server->movetype)
2382 case MOVETYPE_FAKEPUSH:
2383 SV_Physics_Pusher (ent);
2386 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2387 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2390 case MOVETYPE_FOLLOW:
2391 SV_Physics_Follow (ent);
2393 case MOVETYPE_NOCLIP:
2396 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2397 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2400 SV_Physics_Step (ent);
2404 // don't run physics here if running asynchronously
2405 if (host_client->clmovement_inputtimeout <= 0)
2409 case MOVETYPE_BOUNCE:
2410 case MOVETYPE_BOUNCEMISSILE:
2411 case MOVETYPE_FLYMISSILE:
2414 SV_Physics_Toss (ent);
2421 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2425 // decrement the countdown variable used to decide when to go back to
2426 // synchronous physics
2427 if (host_client->clmovement_inputtimeout > sv.frametime)
2428 host_client->clmovement_inputtimeout -= sv.frametime;
2430 host_client->clmovement_inputtimeout = 0;
2432 SV_CheckVelocity (ent);
2434 SV_LinkEdict (ent, true);
2436 SV_CheckVelocity (ent);
2438 // call standard player post-think
2439 prog->globals.server->time = sv.time;
2440 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2441 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2443 if(ent->fields.server->fixangle)
2445 // angle fixing was requested by physics code...
2446 // so store the current angles for later use
2447 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2448 host_client->fixangle_angles_set = TRUE;
2450 // and clear fixangle for the next frame
2451 ent->fields.server->fixangle = 0;
2461 void SV_Physics (void)
2466 // let the progs know that a new frame has started
2467 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2468 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2469 prog->globals.server->time = sv.time;
2470 prog->globals.server->frametime = sv.frametime;
2471 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2474 // treat each object in turn
2477 // if force_retouch, relink all the entities
2478 if (prog->globals.server->force_retouch > 0)
2479 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2480 if (!ent->priv.server->free)
2481 SV_LinkEdict (ent, true); // force retouch even for stationary
2483 // run physics on the client entities
2484 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2485 if (!ent->priv.server->free)
2486 SV_Physics_ClientEntity(ent);
2488 // run physics on all the non-client entities
2489 if (!sv_freezenonclients.integer)
2491 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2492 if (!ent->priv.server->free)
2493 SV_Physics_Entity(ent);
2494 // make a second pass to see if any ents spawned this frame and make
2495 // sure they run their move/think
2496 if (sv_gameplayfix_delayprojectiles.integer < 0)
2497 for (i = svs.maxclients + 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2498 if (!ent->priv.server->move && !ent->priv.server->free)
2499 SV_Physics_Entity(ent);
2502 if (prog->globals.server->force_retouch > 0)
2503 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2505 // LordHavoc: endframe support
2506 if (prog->funcoffsets.EndFrame)
2508 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2509 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2510 prog->globals.server->time = sv.time;
2511 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2514 // decrement prog->num_edicts if the highest number entities died
2515 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2517 if (!sv_freezenonclients.integer)
2518 sv.time += sv.frametime;