2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 #define MOVE_EPSILON 0.01
44 void SV_Physics_Toss (prvm_edict_t *ent);
47 ===============================================================================
51 ===============================================================================
54 int SV_GenericHitSuperContentsMask(const prvm_edict_t *passedict)
59 val = PRVM_EDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
60 if (val && val->_float)
61 return (int)val->_float;
62 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
64 if ((int)passedict->fields.server->flags & FL_MONSTER)
65 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
67 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
69 else if (passedict->fields.server->solid == SOLID_CORPSE)
70 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
71 else if (passedict->fields.server->solid == SOLID_TRIGGER)
72 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
74 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
77 return SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
85 #if COLLISIONPARANOID >= 1
86 trace_t SV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
88 trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict, int hitsupercontentsmask)
91 vec3_t hullmins, hullmaxs;
92 int i, bodysupercontents;
96 prvm_edict_t *traceowner, *touch;
98 // bounding box of entire move area
99 vec3_t clipboxmins, clipboxmaxs;
100 // size of the moving object
101 vec3_t clipmins, clipmaxs;
102 // size when clipping against monsters
103 vec3_t clipmins2, clipmaxs2;
104 // start and end origin of move
105 vec3_t clipstart, clipend;
108 // matrices to transform into/out of other entity's space
109 matrix4x4_t matrix, imatrix;
110 // model of other entity
112 // list of entities to test for collisions
114 prvm_edict_t *touchedicts[MAX_EDICTS];
116 VectorCopy(start, clipstart);
117 VectorCopy(end, clipend);
118 VectorCopy(mins, clipmins);
119 VectorCopy(maxs, clipmaxs);
120 VectorCopy(mins, clipmins2);
121 VectorCopy(maxs, clipmaxs2);
122 #if COLLISIONPARANOID >= 3
123 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
127 Collision_ClipToWorld(&cliptrace, sv.worldmodel, clipstart, clipmins, clipmaxs, clipend, hitsupercontentsmask);
128 cliptrace.bmodelstartsolid = cliptrace.startsolid;
129 if (cliptrace.startsolid || cliptrace.fraction < 1)
130 cliptrace.ent = prog->edicts;
131 if (type == MOVE_WORLDONLY)
134 if (type == MOVE_MISSILE)
136 // LordHavoc: modified this, was = -15, now -= 15
137 for (i = 0;i < 3;i++)
144 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
145 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
146 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
149 VectorCopy(clipmins, hullmins);
150 VectorCopy(clipmaxs, hullmaxs);
153 // create the bounding box of the entire move
154 for (i = 0;i < 3;i++)
156 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
157 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
160 // debug override to test against everything
161 if (sv_debugmove.integer)
163 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
164 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
167 // if the passedict is world, make it NULL (to avoid two checks each time)
168 if (passedict == prog->edicts)
170 // precalculate prog value for passedict for comparisons
171 passedictprog = PRVM_EDICT_TO_PROG(passedict);
172 // figure out whether this is a point trace for comparisons
173 pointtrace = VectorCompare(clipmins, clipmaxs);
174 // precalculate passedict's owner edict pointer for comparisons
175 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
178 // because this uses World_EntitiestoBox, we know all entity boxes overlap
179 // the clip region, so we can skip culling checks in the loop below
180 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
181 if (numtouchedicts > MAX_EDICTS)
183 // this never happens
184 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
185 numtouchedicts = MAX_EDICTS;
187 for (i = 0;i < numtouchedicts;i++)
189 touch = touchedicts[i];
191 if (touch->fields.server->solid < SOLID_BBOX)
193 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
198 // don't clip against self
199 if (passedict == touch)
201 // don't clip owned entities against owner
202 if (traceowner == touch)
204 // don't clip owner against owned entities
205 if (passedictprog == touch->fields.server->owner)
207 // don't clip points against points (they can't collide)
208 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
212 bodysupercontents = touch->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY;
214 // might interact, so do an exact clip
216 if ((int) touch->fields.server->solid == SOLID_BSP || type == MOVE_HITMODEL)
218 unsigned int modelindex = (unsigned int)touch->fields.server->modelindex;
219 // if the modelindex is 0, it shouldn't be SOLID_BSP!
220 if (modelindex > 0 && modelindex < MAX_MODELS)
221 model = sv.models[(int)touch->fields.server->modelindex];
224 ((modelindex = (int)touch->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS && (model = sv.models[(int)touch->fields.server->modelindex]))
226 model->type == mod_alias
229 (((unsigned char)PRVM_EDICTFIELDVALUE(touch, prog->fieldoffsets.pflags)->_float) & PFLAGS_FULLDYNAMIC)
231 ((gamemode == GAME_TENEBRAE) && ((unsigned int)touch->fields.server->effects & (16 | 32)))
237 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);
239 Matrix4x4_CreateTranslate(&matrix, touch->fields.server->origin[0], touch->fields.server->origin[1], touch->fields.server->origin[2]);
240 Matrix4x4_Invert_Simple(&imatrix, &matrix);
241 if ((int)touch->fields.server->flags & FL_MONSTER)
242 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);
244 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);
246 Collision_CombineTraces(&cliptrace, &trace, (void *)touch, touch->fields.server->solid == SOLID_BSP);
252 #if COLLISIONPARANOID >= 1
253 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)
258 trace = SV_Move_(start, mins, maxs, end, type, passedict, hitsupercontentsmask);
261 VectorCopy(trace.endpos, temp);
262 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict, hitsupercontentsmask).startsolid;
263 #if COLLISIONPARANOID < 3
264 if (trace.startsolid || endstuck)
266 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" : "");
272 int SV_PointSuperContents(const vec3_t point)
274 int supercontents = 0;
278 // matrices to transform into/out of other entity's space
279 matrix4x4_t matrix, imatrix;
280 // model of other entity
282 unsigned int modelindex;
284 // list of entities to test for collisions
286 prvm_edict_t *touchedicts[MAX_EDICTS];
288 // get world supercontents at this point
289 if (sv.worldmodel && sv.worldmodel->PointSuperContents)
290 supercontents = sv.worldmodel->PointSuperContents(sv.worldmodel, 0, point);
292 // if sv_gameplayfix_swiminbmodels is off we're done
293 if (!sv_gameplayfix_swiminbmodels.integer)
294 return supercontents;
296 // get list of entities at this point
297 numtouchedicts = World_EntitiesInBox(&sv.world, point, point, MAX_EDICTS, touchedicts);
298 if (numtouchedicts > MAX_EDICTS)
300 // this never happens
301 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
302 numtouchedicts = MAX_EDICTS;
304 for (i = 0;i < numtouchedicts;i++)
306 touch = touchedicts[i];
308 // we only care about SOLID_BSP for pointcontents
309 if (touch->fields.server->solid != SOLID_BSP)
312 // might interact, so do an exact clip
313 modelindex = (unsigned int)touch->fields.server->modelindex;
314 if (modelindex >= MAX_MODELS)
316 model = sv.models[(int)touch->fields.server->modelindex];
317 if (!model || !model->PointSuperContents)
319 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);
320 Matrix4x4_Invert_Simple(&imatrix, &matrix);
321 Matrix4x4_Transform(&imatrix, point, transformed);
322 frame = (int)touch->fields.server->frame;
323 supercontents |= model->PointSuperContents(model, bound(0, frame, (model->numframes - 1)), transformed);
326 return supercontents;
330 ===============================================================================
332 Linking entities into the world culling system
334 ===============================================================================
337 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
339 int i, numtouchedicts, old_self, old_other;
340 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
342 // build a list of edicts to touch, because the link loop can be corrupted
343 // by IncreaseEdicts called during touch functions
344 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
345 if (numtouchedicts > MAX_EDICTS)
347 // this never happens
348 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
349 numtouchedicts = MAX_EDICTS;
352 old_self = prog->globals.server->self;
353 old_other = prog->globals.server->other;
354 for (i = 0;i < numtouchedicts;i++)
356 touch = touchedicts[i];
357 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
360 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
361 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
362 prog->globals.server->time = sv.time;
363 prog->globals.server->trace_allsolid = false;
364 prog->globals.server->trace_startsolid = false;
365 prog->globals.server->trace_fraction = 1;
366 prog->globals.server->trace_inwater = false;
367 prog->globals.server->trace_inopen = true;
368 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
369 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
370 prog->globals.server->trace_plane_dist = 0;
371 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
372 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
374 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
376 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
378 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
380 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
383 prog->globals.server->self = old_self;
384 prog->globals.server->other = old_other;
393 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
398 if (ent == prog->edicts)
399 return; // don't add the world
401 if (ent->priv.server->free)
406 if (ent->fields.server->solid == SOLID_BSP)
408 int modelindex = (int)ent->fields.server->modelindex;
409 if (modelindex < 0 || modelindex >= MAX_MODELS)
411 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
414 model = sv.models[modelindex];
417 if (!model->TraceBox && developer.integer >= 1)
418 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
420 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
422 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
423 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
425 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
427 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
428 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
432 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
433 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
438 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
439 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
440 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
445 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
446 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
450 // to make items easier to pick up and allow them to be grabbed off
451 // of shelves, the abs sizes are expanded
453 if ((int)ent->fields.server->flags & FL_ITEM)
464 // because movement is clipped an epsilon away from an actual edge,
465 // we must fully check even when bounding boxes don't quite touch
474 VectorCopy(mins, ent->fields.server->absmin);
475 VectorCopy(maxs, ent->fields.server->absmax);
477 World_LinkEdict(&sv.world, ent, mins, maxs);
479 // if touch_triggers, call touch on all entities overlapping this box
480 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
481 SV_LinkEdict_TouchAreaGrid(ent);
485 ===============================================================================
489 ===============================================================================
494 SV_TestEntityPosition
496 returns true if the entity is in solid currently
499 static int SV_TestEntityPosition (prvm_edict_t *ent, vec3_t offset)
503 VectorAdd(ent->fields.server->origin, offset, org);
504 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
505 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
509 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
511 // q1bsp/hlbsp use hulls and if the entity does not exactly match
512 // a hull size it is incorrectly tested, so this code tries to
513 // 'fix' it slightly...
514 // FIXME: this breaks entities larger than the hull size
517 VectorAdd(org, ent->fields.server->mins, m1);
518 VectorAdd(org, ent->fields.server->maxs, m2);
519 VectorSubtract(m2, m1, s);
520 #define EPSILON (1.0f / 32.0f)
521 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
522 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
523 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
524 for (i = 0;i < 8;i++)
526 v[0] = (i & 1) ? m2[0] : m1[0];
527 v[1] = (i & 2) ? m2[1] : m1[1];
528 v[2] = (i & 4) ? m2[2] : m1[2];
529 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
534 // if the trace found a better position for the entity, move it there
535 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
538 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
539 VectorCopy(trace.endpos, ent->fields.server->origin);
541 // verify if the endpos is REALLY outside solid
542 VectorCopy(trace.endpos, org);
543 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, SUPERCONTENTS_SOLID);
545 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
547 VectorCopy(org, ent->fields.server->origin);
558 void SV_CheckAllEnts (void)
563 // see if any solid entities are inside the final position
564 check = PRVM_NEXT_EDICT(prog->edicts);
565 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
567 if (check->priv.server->free)
569 if (check->fields.server->movetype == MOVETYPE_PUSH
570 || check->fields.server->movetype == MOVETYPE_NONE
571 || check->fields.server->movetype == MOVETYPE_FOLLOW
572 || check->fields.server->movetype == MOVETYPE_NOCLIP)
575 if (SV_TestEntityPosition (check, vec3_origin))
576 Con_Print("entity in invalid position\n");
580 // DRESK - Support for Entity Contents Transition Event
583 SV_CheckContentsTransition
585 returns true if entity had a valid contentstransition function call
588 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
590 int bValidFunctionCall;
591 prvm_eval_t *contentstransition;
593 // Default Valid Function Call to False
594 bValidFunctionCall = false;
596 if(ent->fields.server->watertype != nContents)
597 { // Changed Contents
598 // Acquire Contents Transition Function from QC
599 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
601 if(contentstransition->function)
602 { // Valid Function; Execute
603 // Assign Valid Function
604 bValidFunctionCall = true;
605 // Prepare Parameters (Original Contents, New Contents)
607 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
609 PRVM_G_FLOAT(OFS_PARM1) = nContents;
611 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
612 // Execute VM Function
613 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
617 // Return if Function Call was Valid
618 return bValidFunctionCall;
627 void SV_CheckVelocity (prvm_edict_t *ent)
635 for (i=0 ; i<3 ; i++)
637 if (IS_NAN(ent->fields.server->velocity[i]))
639 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
640 ent->fields.server->velocity[i] = 0;
642 if (IS_NAN(ent->fields.server->origin[i]))
644 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
645 ent->fields.server->origin[i] = 0;
649 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
650 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
651 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
653 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
654 ent->fields.server->velocity[0] *= wishspeed;
655 ent->fields.server->velocity[1] *= wishspeed;
656 ent->fields.server->velocity[2] *= wishspeed;
664 Runs thinking code if time. There is some play in the exact time the think
665 function will be called, because it is called before any movement is done
666 in a frame. Not used for pushmove objects, because they must be exact.
667 Returns false if the entity removed itself.
670 qboolean SV_RunThink (prvm_edict_t *ent)
674 // don't let things stay in the past.
675 // it is possible to start that way by a trigger with a local time.
676 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
679 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
681 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
682 ent->fields.server->nextthink = 0;
683 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
684 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
685 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
686 // mods often set nextthink to time to cause a think every frame,
687 // we don't want to loop in that case, so exit if the new nextthink is
688 // <= the time the qc was told, also exit if it is past the end of the
690 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
693 return !ent->priv.server->free;
700 Two entities have touched, so run their touch functions
701 returns true if the impact kept the origin of the touching entity intact
704 extern void VM_SetTraceGlobals(const trace_t *trace);
705 extern sizebuf_t vm_tempstringsbuf;
706 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
708 int restorevm_tempstringsbuf_cursize;
709 int old_self, old_other;
711 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
714 old_self = prog->globals.server->self;
715 old_other = prog->globals.server->other;
716 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
718 VectorCopy(e1->fields.server->origin, org);
720 VM_SetTraceGlobals(trace);
722 prog->globals.server->time = sv.time;
723 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
725 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
726 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
727 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
730 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
732 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
733 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
734 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
735 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
736 prog->globals.server->trace_plane_dist = -trace->plane.dist;
737 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
738 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
740 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
742 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
744 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
746 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
749 prog->globals.server->self = old_self;
750 prog->globals.server->other = old_other;
751 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
753 return VectorCompare(e1->fields.server->origin, org);
761 Slide off of the impacting object
762 returns the blocked flags (1 = floor, 2 = step / wall)
765 #define STOP_EPSILON 0.1
766 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
771 backoff = -DotProduct (in, normal) * overbounce;
772 VectorMA(in, backoff, normal, out);
774 for (i = 0;i < 3;i++)
775 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
784 The basic solid body movement clip that slides along multiple planes
785 Returns the clipflags if the velocity was modified (hit something solid)
789 8 = teleported by touch method
790 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
793 static float SV_Gravity (prvm_edict_t *ent);
794 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
795 // LordHavoc: increased from 5 to 32
796 #define MAX_CLIP_PLANES 32
797 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
799 int blocked, bumpcount;
801 float d, time_left, gravity;
802 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
812 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
814 gravity = SV_Gravity(ent) * 0.5f;
815 ent->fields.server->velocity[2] -= gravity;
819 applygravity = false;
820 ent->fields.server->velocity[2] -= SV_Gravity(ent);
824 VectorCopy(ent->fields.server->velocity, original_velocity);
825 VectorCopy(ent->fields.server->velocity, primal_velocity);
828 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
830 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
833 VectorScale(ent->fields.server->velocity, time_left, push);
835 VectorAdd(ent->fields.server->origin, push, end);
837 if(!SV_PushEntity(&trace, ent, push, false, false))
839 // we got teleported by a touch function
840 // let's abort the move
846 //if (trace.fraction < 0.002)
851 VectorCopy(ent->fields.server->origin, start);
852 start[2] += 3;//0.03125;
853 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
854 end[2] += 3;//0.03125;
855 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
856 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)))
858 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
864 for (i = 0;i < numplanes;i++)
866 VectorCopy(ent->fields.server->origin, start);
867 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
868 VectorMA(start, 3, planes[i], start);
869 VectorMA(end, 3, planes[i], end);
870 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
871 if (trace.fraction < testtrace.fraction)
874 VectorCopy(start, ent->fields.server->origin);
879 // VectorAdd(ent->fields.server->origin, planes[j], start);
885 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);
886 if (trace.fraction < 1)
887 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
892 if (trace.bmodelstartsolid)
894 // LordHavoc: note: this code is what makes entities stick in place
895 // if embedded in world only (you can walk through other objects if
897 // entity is trapped in another solid
898 VectorClear(ent->fields.server->velocity);
903 if (trace.fraction == 1)
905 if (trace.plane.normal[2])
907 if (trace.plane.normal[2] > 0.7)
914 Con_Printf ("SV_FlyMove: !trace.ent");
915 trace.ent = prog->edicts;
918 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
919 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
926 // save the trace for player extrafriction
928 VectorCopy(trace.plane.normal, stepnormal);
930 if (trace.fraction >= 0.001)
932 // actually covered some distance
933 VectorCopy(ent->fields.server->velocity, original_velocity);
937 time_left *= 1 - trace.fraction;
939 // clipped to another plane
940 if (numplanes >= MAX_CLIP_PLANES)
942 // this shouldn't really happen
943 VectorClear(ent->fields.server->velocity);
949 for (i = 0;i < numplanes;i++)
950 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
954 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
959 VectorCopy(trace.plane.normal, planes[numplanes]);
962 if (sv_newflymove.integer)
963 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
966 // modify original_velocity so it parallels all of the clip planes
967 for (i = 0;i < numplanes;i++)
969 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
970 for (j = 0;j < numplanes;j++)
975 if (DotProduct(new_velocity, planes[j]) < 0)
985 // go along this plane
986 VectorCopy(new_velocity, ent->fields.server->velocity);
990 // go along the crease
993 VectorClear(ent->fields.server->velocity);
997 CrossProduct(planes[0], planes[1], dir);
998 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
999 VectorNormalize(dir);
1000 d = DotProduct(dir, ent->fields.server->velocity);
1001 VectorScale(dir, d, ent->fields.server->velocity);
1005 // if current velocity is against the original velocity,
1006 // stop dead to avoid tiny occilations in sloping corners
1007 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1009 VectorClear(ent->fields.server->velocity);
1014 //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]);
1017 if ((blocked & 1) == 0 && bumpcount > 1)
1019 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1020 // flag ONGROUND if there's ground under it
1021 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1025 // LordHavoc: this came from QW and allows you to get out of water more easily
1026 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1027 VectorCopy(primal_velocity, ent->fields.server->velocity);
1028 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1029 ent->fields.server->velocity[2] -= gravity;
1039 static float SV_Gravity (prvm_edict_t *ent)
1044 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1045 if (val!=0 && val->_float)
1046 ent_gravity = val->_float;
1049 return ent_gravity * sv_gravity.value * sv.frametime;
1054 ===============================================================================
1058 ===============================================================================
1065 Does not change the entities velocity at all
1066 The trace struct is filled with the trace that has been done.
1067 Returns true if the push did not result in the entity being teleported by QC code.
1070 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1076 VectorAdd (ent->fields.server->origin, push, end);
1078 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1079 type = MOVE_MISSILE;
1080 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1081 type = MOVE_NOMONSTERS; // only clip against bmodels
1085 *trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1086 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1089 VectorCopy (trace->endpos, ent->fields.server->origin);
1092 if(!trace->startsolid)
1093 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)
1095 Con_Printf("something eeeeevil happened\n");
1099 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)));
1103 SV_LinkEdict (ent, dolink);
1104 return SV_Impact (ent, trace);
1107 SV_LinkEdict (ent, true);
1119 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1122 int pusherowner, pusherprog;
1125 float savesolid, movetime2, pushltime;
1126 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1128 int numcheckentities;
1129 static prvm_edict_t *checkentities[MAX_EDICTS];
1130 dp_model_t *pushermodel;
1131 trace_t trace, trace2;
1132 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1133 unsigned short moved_edicts[MAX_EDICTS];
1135 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])
1137 pusher->fields.server->ltime += movetime;
1141 switch ((int) pusher->fields.server->solid)
1143 // LordHavoc: valid pusher types
1146 case SOLID_SLIDEBOX:
1147 case SOLID_CORPSE: // LordHavoc: this would be weird...
1149 // LordHavoc: no collisions
1152 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1153 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1154 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1155 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1156 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1157 pusher->fields.server->ltime += movetime;
1158 SV_LinkEdict (pusher, false);
1161 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1164 index = (int) pusher->fields.server->modelindex;
1165 if (index < 1 || index >= MAX_MODELS)
1167 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1170 pushermodel = sv.models[index];
1171 pusherowner = pusher->fields.server->owner;
1172 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1174 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1176 movetime2 = movetime;
1177 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1178 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1179 if (moveangle[0] || moveangle[2])
1181 for (i = 0;i < 3;i++)
1185 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1186 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1190 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1191 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1195 else if (moveangle[1])
1197 for (i = 0;i < 3;i++)
1201 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1202 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1206 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1207 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1213 for (i = 0;i < 3;i++)
1217 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1218 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1222 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1223 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1228 VectorNegate (moveangle, a);
1229 AngleVectorsFLU (a, forward, left, up);
1231 VectorCopy (pusher->fields.server->origin, pushorig);
1232 VectorCopy (pusher->fields.server->angles, pushang);
1233 pushltime = pusher->fields.server->ltime;
1235 // move the pusher to its final position
1237 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1238 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1239 pusher->fields.server->ltime += movetime;
1240 SV_LinkEdict (pusher, false);
1243 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1244 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1245 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);
1246 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1248 savesolid = pusher->fields.server->solid;
1250 // see if any solid entities are inside the final position
1253 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1254 for (e = 0;e < numcheckentities;e++)
1256 prvm_edict_t *check = checkentities[e];
1257 if (check->fields.server->movetype == MOVETYPE_NONE
1258 || check->fields.server->movetype == MOVETYPE_PUSH
1259 || check->fields.server->movetype == MOVETYPE_FOLLOW
1260 || check->fields.server->movetype == MOVETYPE_NOCLIP
1261 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1264 if (check->fields.server->owner == pusherprog)
1267 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1270 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1272 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1273 check->priv.server->waterposition_forceupdate = true;
1275 checkcontents = SV_GenericHitSuperContentsMask(check);
1277 // if the entity is standing on the pusher, it will definitely be moved
1278 // if the entity is not standing on the pusher, but is in the pusher's
1279 // final position, move it
1280 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1282 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);
1283 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1284 if (!trace.startsolid)
1286 //Con_Printf("- not in solid\n");
1294 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1295 org2[0] = DotProduct (org, forward);
1296 org2[1] = DotProduct (org, left);
1297 org2[2] = DotProduct (org, up);
1298 VectorSubtract (org2, org, move);
1299 VectorAdd (move, move1, move);
1302 VectorCopy (move1, move);
1304 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1306 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1307 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1308 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1310 // try moving the contacted entity
1311 pusher->fields.server->solid = SOLID_NOT;
1312 if(!SV_PushEntity (&trace, check, move, true, true))
1314 // entity "check" got teleported
1315 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1316 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1317 continue; // pushed enough
1319 // FIXME: turn players specially
1320 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1321 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1322 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1324 // this trace.fraction < 1 check causes items to fall off of pushers
1325 // if they pass under or through a wall
1326 // the groundentity check causes items to fall off of ledges
1327 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1328 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1330 // if it is still inside the pusher, block
1331 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);
1332 if (trace.startsolid)
1334 // try moving the contacted entity a tiny bit further to account for precision errors
1336 pusher->fields.server->solid = SOLID_NOT;
1337 VectorScale(move, 1.1, move2);
1338 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1339 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1340 if(!SV_PushEntity (&trace2, check, move2, true, true))
1342 // entity "check" got teleported
1345 pusher->fields.server->solid = savesolid;
1346 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);
1347 if (trace.startsolid)
1349 // try moving the contacted entity a tiny bit less to account for precision errors
1350 pusher->fields.server->solid = SOLID_NOT;
1351 VectorScale(move, 0.9, move2);
1352 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1353 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1354 if(!SV_PushEntity (&trace2, check, move2, true, true))
1356 // entity "check" got teleported
1359 pusher->fields.server->solid = savesolid;
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 // still inside pusher, so it's really blocked
1366 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1368 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1371 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1372 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1376 VectorCopy (pushorig, pusher->fields.server->origin);
1377 VectorCopy (pushang, pusher->fields.server->angles);
1378 pusher->fields.server->ltime = pushltime;
1379 SV_LinkEdict (pusher, false);
1381 // move back any entities we already moved
1382 for (i = 0;i < num_moved;i++)
1384 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1385 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1386 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1387 SV_LinkEdict (ed, false);
1390 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1391 if (pusher->fields.server->blocked)
1393 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1394 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1395 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1402 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1403 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1404 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1413 void SV_Physics_Pusher (prvm_edict_t *ent)
1415 float thinktime, oldltime, movetime;
1417 oldltime = ent->fields.server->ltime;
1419 thinktime = ent->fields.server->nextthink;
1420 if (thinktime < ent->fields.server->ltime + sv.frametime)
1422 movetime = thinktime - ent->fields.server->ltime;
1427 movetime = sv.frametime;
1430 // advances ent->fields.server->ltime if not blocked
1431 SV_PushMove (ent, movetime);
1433 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1435 ent->fields.server->nextthink = 0;
1436 prog->globals.server->time = sv.time;
1437 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1438 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1439 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1445 ===============================================================================
1449 ===============================================================================
1452 static float unstickoffsets[] =
1454 // poutting -/+z changes first as they are least weird
1469 typedef enum unstickresult_e
1477 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1481 // if not stuck in a bmodel, just return
1482 if (!SV_TestEntityPosition(ent, vec3_origin))
1483 return UNSTICK_GOOD;
1485 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1487 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1489 VectorCopy(unstickoffsets + i, offset);
1490 SV_LinkEdict (ent, true);
1491 return UNSTICK_UNSTUCK;
1495 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1496 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1498 for(i = 2; i <= maxunstick; ++i)
1500 VectorClear(offset);
1502 if (!SV_TestEntityPosition(ent, offset))
1504 SV_LinkEdict (ent, true);
1505 return UNSTICK_UNSTUCK;
1508 if (!SV_TestEntityPosition(ent, offset))
1510 SV_LinkEdict (ent, true);
1511 return UNSTICK_UNSTUCK;
1515 return UNSTICK_STUCK;
1518 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1521 switch(SV_UnstickEntityReturnOffset(ent, offset))
1525 case UNSTICK_UNSTUCK:
1526 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]);
1529 if (developer.integer >= 100)
1530 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1533 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1542 This is a big hack to try and fix the rare case of getting stuck in the world
1546 void SV_CheckStuck (prvm_edict_t *ent)
1550 switch(SV_UnstickEntityReturnOffset(ent, offset))
1553 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1555 case UNSTICK_UNSTUCK:
1556 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]);
1559 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1560 if (!SV_TestEntityPosition(ent, offset))
1562 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1563 SV_LinkEdict (ent, true);
1566 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1569 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1579 qboolean SV_CheckWater (prvm_edict_t *ent)
1582 int nNativeContents;
1585 point[0] = ent->fields.server->origin[0];
1586 point[1] = ent->fields.server->origin[1];
1587 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1589 // DRESK - Support for Entity Contents Transition Event
1590 // NOTE: Some logic needed to be slightly re-ordered
1591 // to not affect performance and allow for the feature.
1593 // Acquire Super Contents Prior to Resets
1594 cont = SV_PointSuperContents(point);
1595 // Acquire Native Contents Here
1596 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1598 // DRESK - Support for Entity Contents Transition Event
1599 if(ent->fields.server->watertype)
1600 // Entity did NOT Spawn; Check
1601 SV_CheckContentsTransition(ent, nNativeContents);
1604 ent->fields.server->waterlevel = 0;
1605 ent->fields.server->watertype = CONTENTS_EMPTY;
1606 cont = SV_PointSuperContents(point);
1607 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1609 ent->fields.server->watertype = nNativeContents;
1610 ent->fields.server->waterlevel = 1;
1611 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1612 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1614 ent->fields.server->waterlevel = 2;
1615 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1616 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1617 ent->fields.server->waterlevel = 3;
1621 return ent->fields.server->waterlevel > 1;
1630 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1633 vec3_t forward, into, side;
1635 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1636 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1638 // cut the tangential velocity
1639 i = DotProduct (stepnormal, ent->fields.server->velocity);
1640 VectorScale (stepnormal, i, into);
1641 VectorSubtract (ent->fields.server->velocity, into, side);
1642 ent->fields.server->velocity[0] = side[0] * (1 + d);
1643 ent->fields.server->velocity[1] = side[1] * (1 + d);
1649 =====================
1652 Player has come to a dead stop, possibly due to the problem with limited
1653 float precision at some angle joins in the BSP hull.
1655 Try fixing by pushing one pixel in each direction.
1657 This is a hack, but in the interest of good gameplay...
1658 ======================
1660 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1665 VectorCopy (ent->fields.server->origin, oldorg);
1668 for (i=0 ; i<8 ; i++)
1670 // try pushing a little in an axial direction
1673 case 0: dir[0] = 2; dir[1] = 0; break;
1674 case 1: dir[0] = 0; dir[1] = 2; break;
1675 case 2: dir[0] = -2; dir[1] = 0; break;
1676 case 3: dir[0] = 0; dir[1] = -2; break;
1677 case 4: dir[0] = 2; dir[1] = 2; break;
1678 case 5: dir[0] = -2; dir[1] = 2; break;
1679 case 6: dir[0] = 2; dir[1] = -2; break;
1680 case 7: dir[0] = -2; dir[1] = -2; break;
1683 SV_PushEntity (&trace, ent, dir, false, true);
1685 // retry the original move
1686 ent->fields.server->velocity[0] = oldvel[0];
1687 ent->fields.server->velocity[1] = oldvel[1];
1688 ent->fields.server->velocity[2] = 0;
1689 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1691 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1692 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1694 Con_DPrint("TryUnstick - success.\n");
1698 // go back to the original pos and try again
1699 VectorCopy (oldorg, ent->fields.server->origin);
1703 VectorClear (ent->fields.server->velocity);
1704 Con_DPrint("TryUnstick - failure.\n");
1710 =====================
1713 Only used by players
1714 ======================
1716 void SV_WalkMove (prvm_edict_t *ent)
1718 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1719 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1720 trace_t downtrace, trace;
1721 qboolean applygravity;
1723 // if frametime is 0 (due to client sending the same timestamp twice),
1725 if (sv.frametime <= 0)
1728 SV_CheckStuck (ent);
1730 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1732 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1734 SV_CheckVelocity(ent);
1736 // do a regular slide move unless it looks like you ran into a step
1737 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1739 VectorCopy (ent->fields.server->origin, start_origin);
1740 VectorCopy (ent->fields.server->velocity, start_velocity);
1742 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1744 // if the move did not hit the ground at any point, we're not on ground
1746 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1748 SV_CheckVelocity(ent);
1749 SV_LinkEdict (ent, true);
1751 if(clip & 8) // teleport
1754 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1757 if (sv_nostep.integer)
1760 VectorCopy(ent->fields.server->origin, originalmove_origin);
1761 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1762 originalmove_clip = clip;
1763 originalmove_flags = (int)ent->fields.server->flags;
1764 originalmove_groundentity = ent->fields.server->groundentity;
1766 // if move didn't block on a step, return
1769 // if move was not trying to move into the step, return
1770 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1773 if (ent->fields.server->movetype != MOVETYPE_FLY)
1775 // return if gibbed by a trigger
1776 if (ent->fields.server->movetype != MOVETYPE_WALK)
1779 // only step up while jumping if that is enabled
1780 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1781 if (!oldonground && ent->fields.server->waterlevel == 0)
1785 // try moving up and forward to go up a step
1786 // back to start pos
1787 VectorCopy (start_origin, ent->fields.server->origin);
1788 VectorCopy (start_velocity, ent->fields.server->velocity);
1791 VectorClear (upmove);
1792 upmove[2] = sv_stepheight.value;
1793 if(!SV_PushEntity(&trace, ent, upmove, false, true))
1795 // we got teleported when upstepping... must abort the move
1800 ent->fields.server->velocity[2] = 0;
1801 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1802 ent->fields.server->velocity[2] += start_velocity[2];
1805 // we got teleported when upstepping... must abort the move
1806 // note that z velocity handling may not be what QC expects here, but we cannot help it
1810 SV_CheckVelocity(ent);
1811 SV_LinkEdict (ent, true);
1813 // check for stuckness, possibly due to the limited precision of floats
1814 // in the clipping hulls
1816 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1817 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1819 //Con_Printf("wall\n");
1820 // stepping up didn't make any progress, revert to original move
1821 VectorCopy(originalmove_origin, ent->fields.server->origin);
1822 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1823 //clip = originalmove_clip;
1824 ent->fields.server->flags = originalmove_flags;
1825 ent->fields.server->groundentity = originalmove_groundentity;
1826 // now try to unstick if needed
1827 //clip = SV_TryUnstick (ent, oldvel);
1831 //Con_Printf("step - ");
1833 // extra friction based on view angle
1834 if (clip & 2 && sv_wallfriction.integer)
1835 SV_WallFriction (ent, stepnormal);
1837 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1838 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))
1842 VectorClear (downmove);
1843 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1844 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
1846 // we got teleported when downstepping... must abort the move
1850 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1852 // this has been disabled so that you can't jump when you are stepping
1853 // up while already jumping (also known as the Quake2 double jump bug)
1855 // LordHavoc: disabled this check so you can walk on monsters/players
1856 //if (ent->fields.server->solid == SOLID_BSP)
1858 //Con_Printf("onground\n");
1859 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1860 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1866 //Con_Printf("slope\n");
1867 // if the push down didn't end up on good ground, use the move without
1868 // the step up. This happens near wall / slope combinations, and can
1869 // cause the player to hop up higher on a slope too steep to climb
1870 VectorCopy(originalmove_origin, ent->fields.server->origin);
1871 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1872 //clip = originalmove_clip;
1873 ent->fields.server->flags = originalmove_flags;
1874 ent->fields.server->groundentity = originalmove_groundentity;
1877 SV_CheckVelocity(ent);
1878 SV_LinkEdict (ent, true);
1881 //============================================================================
1887 Entities that are "stuck" to another entity
1890 void SV_Physics_Follow (prvm_edict_t *ent)
1892 vec3_t vf, vr, vu, angles, v;
1896 if (!SV_RunThink (ent))
1899 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1900 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1901 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])
1903 // quick case for no rotation
1904 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1908 angles[0] = -ent->fields.server->punchangle[0];
1909 angles[1] = ent->fields.server->punchangle[1];
1910 angles[2] = ent->fields.server->punchangle[2];
1911 AngleVectors (angles, vf, vr, vu);
1912 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];
1913 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];
1914 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];
1915 angles[0] = -e->fields.server->angles[0];
1916 angles[1] = e->fields.server->angles[1];
1917 angles[2] = e->fields.server->angles[2];
1918 AngleVectors (angles, vf, vr, vu);
1919 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1920 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1921 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1923 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1924 SV_LinkEdict (ent, true);
1928 ==============================================================================
1932 ==============================================================================
1937 SV_CheckWaterTransition
1941 void SV_CheckWaterTransition (prvm_edict_t *ent)
1944 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1945 if (!ent->fields.server->watertype)
1947 // just spawned here
1948 ent->fields.server->watertype = cont;
1949 ent->fields.server->waterlevel = 1;
1953 // DRESK - Support for Entity Contents Transition Event
1954 // NOTE: Call here BEFORE updating the watertype below,
1955 // and suppress watersplash sound if a valid function
1956 // call was made to allow for custom "splash" sounds.
1957 if( !SV_CheckContentsTransition(ent, cont) )
1958 { // Contents Transition Function Invalid; Potentially Play Water Sound
1959 // check if the entity crossed into or out of water
1960 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1961 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1964 if (cont <= CONTENTS_WATER)
1966 ent->fields.server->watertype = cont;
1967 ent->fields.server->waterlevel = 1;
1971 ent->fields.server->watertype = CONTENTS_EMPTY;
1972 ent->fields.server->waterlevel = 0;
1980 Toss, bounce, and fly movement. When onground, do nothing.
1983 void SV_Physics_Toss (prvm_edict_t *ent)
1990 // if onground, return without moving
1991 if ((int)ent->fields.server->flags & FL_ONGROUND)
1993 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1995 // don't stick to ground if onground and moving upward
1996 ent->fields.server->flags -= FL_ONGROUND;
1998 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2000 // we can trust FL_ONGROUND if groundentity is world because it never moves
2003 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2005 // if ent was supported by a brush model on previous frame,
2006 // and groundentity is now freed, set groundentity to 0 (world)
2007 // which leaves it suspended in the air
2008 ent->fields.server->groundentity = 0;
2012 ent->priv.server->suspendedinairflag = false;
2014 SV_CheckVelocity (ent);
2017 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2018 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2021 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2023 movetime = sv.frametime;
2024 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2027 VectorScale (ent->fields.server->velocity, movetime, move);
2028 if(!SV_PushEntity (&trace, ent, move, true, true))
2029 return; // teleported
2030 if (ent->priv.server->free)
2032 if (trace.bmodelstartsolid)
2034 // try to unstick the entity
2035 SV_UnstickEntity(ent);
2036 if(!SV_PushEntity (&trace, ent, move, false, true))
2037 return; // teleported
2038 if (ent->priv.server->free)
2041 if (trace.fraction == 1)
2043 movetime *= 1 - min(1, trace.fraction);
2044 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2046 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2047 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2049 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2051 float d, ent_gravity;
2053 float bouncefactor = 0.5f;
2054 float bouncestop = 60.0f / 800.0f;
2056 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2057 if (val!=0 && val->_float)
2058 bouncefactor = val->_float;
2060 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2061 if (val!=0 && val->_float)
2062 bouncestop = val->_float;
2064 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2065 // LordHavoc: fixed grenades not bouncing when fired down a slope
2066 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2067 if (val!=0 && val->_float)
2068 ent_gravity = val->_float;
2071 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2073 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2074 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2076 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2077 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2078 VectorClear (ent->fields.server->velocity);
2079 VectorClear (ent->fields.server->avelocity);
2082 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2086 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2088 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2089 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2090 VectorClear (ent->fields.server->velocity);
2091 VectorClear (ent->fields.server->avelocity);
2094 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2099 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2100 if (trace.plane.normal[2] > 0.7)
2102 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2103 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2104 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2105 ent->priv.server->suspendedinairflag = true;
2106 VectorClear (ent->fields.server->velocity);
2107 VectorClear (ent->fields.server->avelocity);
2110 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2112 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2116 // check for in water
2117 SV_CheckWaterTransition (ent);
2121 ===============================================================================
2125 ===============================================================================
2132 Monsters freefall when they don't have a ground entity, otherwise
2133 all movement is done with discrete steps.
2135 This is also used for objects that have become still on the ground, but
2136 will fall if the floor is pulled out from under them.
2139 void SV_Physics_Step (prvm_edict_t *ent)
2141 int flags = (int)ent->fields.server->flags;
2144 // Backup Velocity in the event that movetypesteplandevent is called,
2145 // to provide a parameter with the entity's velocity at impact.
2146 prvm_eval_t *movetypesteplandevent;
2147 vec3_t backupVelocity;
2148 VectorCopy(ent->fields.server->velocity, backupVelocity);
2149 // don't fall at all if fly/swim
2150 if (!(flags & (FL_FLY | FL_SWIM)))
2152 if (flags & FL_ONGROUND)
2154 // freefall if onground and moving upward
2155 // freefall if not standing on a world surface (it may be a lift or trap door)
2156 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2158 ent->fields.server->flags -= FL_ONGROUND;
2159 SV_CheckVelocity(ent);
2160 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2161 SV_LinkEdict(ent, true);
2162 ent->priv.server->waterposition_forceupdate = true;
2167 // freefall if not onground
2168 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2170 SV_CheckVelocity(ent);
2171 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2172 SV_LinkEdict(ent, true);
2175 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2177 // DRESK - Check for Entity Land Event Function
2178 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2180 if(movetypesteplandevent->function)
2181 { // Valid Function; Execute
2182 // Prepare Parameters
2183 // Assign Velocity at Impact
2184 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2185 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2186 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2188 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2189 // Execute VM Function
2190 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2193 // Check for Engine Landing Sound
2194 if(sv_sound_land.string)
2195 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2197 ent->priv.server->waterposition_forceupdate = true;
2202 if (!SV_RunThink(ent))
2205 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2207 ent->priv.server->waterposition_forceupdate = false;
2208 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2209 SV_CheckWaterTransition(ent);
2213 //============================================================================
2215 static void SV_Physics_Entity (prvm_edict_t *ent)
2217 // don't run think/move on newly spawned projectiles as it messes up
2218 // movement interpolation and rocket trails, and is inconsistent with
2219 // respect to entities spawned in the same frame
2220 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2221 // but if it spawns a lower numbered ent, it doesn't - this never moves
2222 // ents in the first frame regardless)
2223 qboolean runmove = ent->priv.server->move;
2224 ent->priv.server->move = true;
2225 if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2227 switch ((int) ent->fields.server->movetype)
2230 case MOVETYPE_FAKEPUSH:
2231 SV_Physics_Pusher (ent);
2234 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2235 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2238 case MOVETYPE_FOLLOW:
2239 SV_Physics_Follow (ent);
2241 case MOVETYPE_NOCLIP:
2242 if (SV_RunThink(ent))
2245 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2246 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2248 SV_LinkEdict(ent, false);
2251 SV_Physics_Step (ent);
2254 if (SV_RunThink (ent))
2258 case MOVETYPE_BOUNCE:
2259 case MOVETYPE_BOUNCEMISSILE:
2260 case MOVETYPE_FLYMISSILE:
2263 if (SV_RunThink (ent))
2264 SV_Physics_Toss (ent);
2267 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2272 void SV_Physics_ClientMove(void)
2275 ent = host_client->edict;
2277 // call player physics, this needs the proper frametime
2278 prog->globals.server->frametime = sv.frametime;
2281 // call standard client pre-think, with frametime = 0
2282 prog->globals.server->time = sv.time;
2283 prog->globals.server->frametime = 0;
2284 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2285 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2286 prog->globals.server->frametime = sv.frametime;
2288 // make sure the velocity is sane (not a NaN)
2289 SV_CheckVelocity(ent);
2290 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2291 // player_run/player_stand1 does not horribly malfunction if the
2292 // velocity becomes a number that is both == 0 and != 0
2293 // (sounds to me like NaN but to be absolutely safe...)
2294 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2295 VectorClear(ent->fields.server->velocity);
2297 // perform MOVETYPE_WALK behavior
2300 // call standard player post-think, with frametime = 0
2301 prog->globals.server->time = sv.time;
2302 prog->globals.server->frametime = 0;
2303 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2304 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2305 prog->globals.server->frametime = sv.frametime;
2307 if(ent->fields.server->fixangle)
2309 // angle fixing was requested by physics code...
2310 // so store the current angles for later use
2311 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2312 host_client->fixangle_angles_set = TRUE;
2314 // and clear fixangle for the next frame
2315 ent->fields.server->fixangle = 0;
2319 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2321 // don't do physics on disconnected clients, FrikBot relies on this
2322 if (!host_client->spawned)
2324 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2328 // don't run physics here if running asynchronously
2329 if (host_client->clmovement_inputtimeout <= 0)
2332 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2335 // make sure the velocity is sane (not a NaN)
2336 SV_CheckVelocity(ent);
2337 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2338 // player_run/player_stand1 does not horribly malfunction if the
2339 // velocity becomes a number that is both == 0 and != 0
2340 // (sounds to me like NaN but to be absolutely safe...)
2341 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2342 VectorClear(ent->fields.server->velocity);
2344 // call standard client pre-think
2345 prog->globals.server->time = sv.time;
2346 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2347 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2348 SV_CheckVelocity (ent);
2350 switch ((int) ent->fields.server->movetype)
2353 case MOVETYPE_FAKEPUSH:
2354 SV_Physics_Pusher (ent);
2357 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2358 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2361 case MOVETYPE_FOLLOW:
2362 SV_Physics_Follow (ent);
2364 case MOVETYPE_NOCLIP:
2367 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2368 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2371 SV_Physics_Step (ent);
2375 // don't run physics here if running asynchronously
2376 if (host_client->clmovement_inputtimeout <= 0)
2380 case MOVETYPE_BOUNCE:
2381 case MOVETYPE_BOUNCEMISSILE:
2382 case MOVETYPE_FLYMISSILE:
2385 SV_Physics_Toss (ent);
2392 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2396 // decrement the countdown variable used to decide when to go back to
2397 // synchronous physics
2398 if (host_client->clmovement_inputtimeout > sv.frametime)
2399 host_client->clmovement_inputtimeout -= sv.frametime;
2401 host_client->clmovement_inputtimeout = 0;
2403 SV_CheckVelocity (ent);
2405 SV_LinkEdict (ent, true);
2407 SV_CheckVelocity (ent);
2409 // call standard player post-think
2410 prog->globals.server->time = sv.time;
2411 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2412 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2414 if(ent->fields.server->fixangle)
2416 // angle fixing was requested by physics code...
2417 // so store the current angles for later use
2418 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2419 host_client->fixangle_angles_set = TRUE;
2421 // and clear fixangle for the next frame
2422 ent->fields.server->fixangle = 0;
2432 void SV_Physics (void)
2437 // let the progs know that a new frame has started
2438 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2439 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2440 prog->globals.server->time = sv.time;
2441 prog->globals.server->frametime = sv.frametime;
2442 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2445 // treat each object in turn
2448 // if force_retouch, relink all the entities
2449 if (prog->globals.server->force_retouch > 0)
2450 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2451 if (!ent->priv.server->free)
2452 SV_LinkEdict (ent, true); // force retouch even for stationary
2454 // run physics on the client entities
2455 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2456 if (!ent->priv.server->free)
2457 SV_Physics_ClientEntity(ent);
2459 // run physics on all the non-client entities
2460 if (!sv_freezenonclients.integer)
2461 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2462 if (!ent->priv.server->free)
2463 SV_Physics_Entity(ent);
2465 if (prog->globals.server->force_retouch > 0)
2466 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2468 // LordHavoc: endframe support
2469 if (prog->funcoffsets.EndFrame)
2471 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2472 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2473 prog->globals.server->time = sv.time;
2474 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2477 // decrement prog->num_edicts if the highest number entities died
2478 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2480 if (!sv_freezenonclients.integer)
2481 sv.time += sv.frametime;