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;
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)
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)
2490 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2491 if (!ent->priv.server->free)
2492 SV_Physics_Entity(ent);
2494 if (prog->globals.server->force_retouch > 0)
2495 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2497 // LordHavoc: endframe support
2498 if (prog->funcoffsets.EndFrame)
2500 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2501 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2502 prog->globals.server->time = sv.time;
2503 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2506 // decrement prog->num_edicts if the highest number entities died
2507 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2509 if (!sv_freezenonclients.integer)
2510 sv.time += sv.frametime;