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)
504 contents = SV_GenericHitSuperContentsMask(ent);
505 VectorAdd(ent->fields.server->origin, offset, org);
506 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent, contents);
507 if (trace.startsupercontents & contents)
511 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
513 // q1bsp/hlbsp use hulls and if the entity does not exactly match
514 // a hull size it is incorrectly tested, so this code tries to
515 // 'fix' it slightly...
516 // FIXME: this breaks entities larger than the hull size
519 VectorAdd(org, ent->fields.server->mins, m1);
520 VectorAdd(org, ent->fields.server->maxs, m2);
521 VectorSubtract(m2, m1, s);
522 #define EPSILON (1.0f / 32.0f)
523 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
524 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
525 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
526 for (i = 0;i < 8;i++)
528 v[0] = (i & 1) ? m2[0] : m1[0];
529 v[1] = (i & 2) ? m2[1] : m1[1];
530 v[2] = (i & 4) ? m2[2] : m1[2];
531 if (SV_PointSuperContents(v) & contents)
536 // if the trace found a better position for the entity, move it there
537 if (VectorDistance2(trace.endpos, ent->fields.server->origin) >= 0.0001)
540 // please switch back to this code when trace.endpos sometimes being in solid bug is fixed
541 VectorCopy(trace.endpos, ent->fields.server->origin);
543 // verify if the endpos is REALLY outside solid
544 VectorCopy(trace.endpos, org);
545 trace = SV_Move (org, ent->fields.server->mins, ent->fields.server->maxs, org, MOVE_NOMONSTERS, ent, contents);
547 Con_Printf("SV_TestEntityPosition: trace.endpos detected to be in solid. NOT using it.\n");
549 VectorCopy(org, ent->fields.server->origin);
560 void SV_CheckAllEnts (void)
565 // see if any solid entities are inside the final position
566 check = PRVM_NEXT_EDICT(prog->edicts);
567 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
569 if (check->priv.server->free)
571 if (check->fields.server->movetype == MOVETYPE_PUSH
572 || check->fields.server->movetype == MOVETYPE_NONE
573 || check->fields.server->movetype == MOVETYPE_FOLLOW
574 || check->fields.server->movetype == MOVETYPE_NOCLIP)
577 if (SV_TestEntityPosition (check, vec3_origin))
578 Con_Print("entity in invalid position\n");
582 // DRESK - Support for Entity Contents Transition Event
585 SV_CheckContentsTransition
587 returns true if entity had a valid contentstransition function call
590 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
592 int bValidFunctionCall;
593 prvm_eval_t *contentstransition;
595 // Default Valid Function Call to False
596 bValidFunctionCall = false;
598 if(ent->fields.server->watertype != nContents)
599 { // Changed Contents
600 // Acquire Contents Transition Function from QC
601 contentstransition = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
603 if(contentstransition->function)
604 { // Valid Function; Execute
605 // Assign Valid Function
606 bValidFunctionCall = true;
607 // Prepare Parameters (Original Contents, New Contents)
609 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
611 PRVM_G_FLOAT(OFS_PARM1) = nContents;
613 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
614 // Execute VM Function
615 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
619 // Return if Function Call was Valid
620 return bValidFunctionCall;
629 void SV_CheckVelocity (prvm_edict_t *ent)
637 for (i=0 ; i<3 ; i++)
639 if (IS_NAN(ent->fields.server->velocity[i]))
641 Con_Printf("Got a NaN velocity on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
642 ent->fields.server->velocity[i] = 0;
644 if (IS_NAN(ent->fields.server->origin[i]))
646 Con_Printf("Got a NaN origin on entity #%i (%s)\n", PRVM_NUM_FOR_EDICT(ent), PRVM_GetString(ent->fields.server->classname));
647 ent->fields.server->origin[i] = 0;
651 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
652 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
653 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
655 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
656 ent->fields.server->velocity[0] *= wishspeed;
657 ent->fields.server->velocity[1] *= wishspeed;
658 ent->fields.server->velocity[2] *= wishspeed;
666 Runs thinking code if time. There is some play in the exact time the think
667 function will be called, because it is called before any movement is done
668 in a frame. Not used for pushmove objects, because they must be exact.
669 Returns false if the entity removed itself.
672 qboolean SV_RunThink (prvm_edict_t *ent)
676 // don't let things stay in the past.
677 // it is possible to start that way by a trigger with a local time.
678 if (ent->fields.server->nextthink <= 0 || ent->fields.server->nextthink > sv.time + sv.frametime)
681 for (iterations = 0;iterations < 128 && !ent->priv.server->free;iterations++)
683 prog->globals.server->time = max(sv.time, ent->fields.server->nextthink);
684 ent->fields.server->nextthink = 0;
685 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
686 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
687 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
688 // mods often set nextthink to time to cause a think every frame,
689 // we don't want to loop in that case, so exit if the new nextthink is
690 // <= the time the qc was told, also exit if it is past the end of the
692 if (ent->fields.server->nextthink <= prog->globals.server->time || ent->fields.server->nextthink > sv.time + sv.frametime || !sv_gameplayfix_multiplethinksperframe.integer)
695 return !ent->priv.server->free;
702 Two entities have touched, so run their touch functions
703 returns true if the impact kept the origin of the touching entity intact
706 extern void VM_SetTraceGlobals(const trace_t *trace);
707 extern sizebuf_t vm_tempstringsbuf;
708 qboolean SV_Impact (prvm_edict_t *e1, trace_t *trace)
710 int restorevm_tempstringsbuf_cursize;
711 int old_self, old_other;
713 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
716 old_self = prog->globals.server->self;
717 old_other = prog->globals.server->other;
718 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
720 VectorCopy(e1->fields.server->origin, org);
722 VM_SetTraceGlobals(trace);
724 prog->globals.server->time = sv.time;
725 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
727 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
728 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
729 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
732 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
734 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
735 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
736 VectorCopy(e2->fields.server->origin, prog->globals.server->trace_endpos);
737 VectorNegate(trace->plane.normal, prog->globals.server->trace_plane_normal);
738 prog->globals.server->trace_plane_dist = -trace->plane.dist;
739 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
740 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
742 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
744 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
746 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
748 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
751 prog->globals.server->self = old_self;
752 prog->globals.server->other = old_other;
753 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
755 return VectorCompare(e1->fields.server->origin, org);
763 Slide off of the impacting object
764 returns the blocked flags (1 = floor, 2 = step / wall)
767 #define STOP_EPSILON 0.1
768 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
773 backoff = -DotProduct (in, normal) * overbounce;
774 VectorMA(in, backoff, normal, out);
776 for (i = 0;i < 3;i++)
777 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
786 The basic solid body movement clip that slides along multiple planes
787 Returns the clipflags if the velocity was modified (hit something solid)
791 8 = teleported by touch method
792 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
795 static float SV_Gravity (prvm_edict_t *ent);
796 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink);
797 // LordHavoc: increased from 5 to 32
798 #define MAX_CLIP_PLANES 32
799 static int SV_FlyMove (prvm_edict_t *ent, float time, qboolean applygravity, float *stepnormal, int hitsupercontentsmask)
801 int blocked, bumpcount;
803 float d, time_left, gravity;
804 vec3_t dir, push, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
814 if (sv_gameplayfix_gravityunaffectedbyticrate.integer)
816 gravity = SV_Gravity(ent) * 0.5f;
817 ent->fields.server->velocity[2] -= gravity;
821 applygravity = false;
822 ent->fields.server->velocity[2] -= SV_Gravity(ent);
826 VectorCopy(ent->fields.server->velocity, original_velocity);
827 VectorCopy(ent->fields.server->velocity, primal_velocity);
830 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
832 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
835 VectorScale(ent->fields.server->velocity, time_left, push);
837 VectorAdd(ent->fields.server->origin, push, end);
839 if(!SV_PushEntity(&trace, ent, push, false, false))
841 // we got teleported by a touch function
842 // let's abort the move
848 //if (trace.fraction < 0.002)
853 VectorCopy(ent->fields.server->origin, start);
854 start[2] += 3;//0.03125;
855 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
856 end[2] += 3;//0.03125;
857 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
858 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)))
860 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
866 for (i = 0;i < numplanes;i++)
868 VectorCopy(ent->fields.server->origin, start);
869 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
870 VectorMA(start, 3, planes[i], start);
871 VectorMA(end, 3, planes[i], end);
872 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
873 if (trace.fraction < testtrace.fraction)
876 VectorCopy(start, ent->fields.server->origin);
881 // VectorAdd(ent->fields.server->origin, planes[j], start);
887 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);
888 if (trace.fraction < 1)
889 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
894 if (trace.bmodelstartsolid)
896 // LordHavoc: note: this code is what makes entities stick in place
897 // if embedded in world only (you can walk through other objects if
899 // entity is trapped in another solid
900 VectorClear(ent->fields.server->velocity);
905 if (trace.fraction == 1)
907 if (trace.plane.normal[2])
909 if (trace.plane.normal[2] > 0.7)
916 Con_Printf ("SV_FlyMove: !trace.ent");
917 trace.ent = prog->edicts;
920 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
921 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
928 // save the trace for player extrafriction
930 VectorCopy(trace.plane.normal, stepnormal);
932 if (trace.fraction >= 0.001)
934 // actually covered some distance
935 VectorCopy(ent->fields.server->velocity, original_velocity);
939 time_left *= 1 - trace.fraction;
941 // clipped to another plane
942 if (numplanes >= MAX_CLIP_PLANES)
944 // this shouldn't really happen
945 VectorClear(ent->fields.server->velocity);
951 for (i = 0;i < numplanes;i++)
952 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
956 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
961 VectorCopy(trace.plane.normal, planes[numplanes]);
964 if (sv_newflymove.integer)
965 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
968 // modify original_velocity so it parallels all of the clip planes
969 for (i = 0;i < numplanes;i++)
971 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
972 for (j = 0;j < numplanes;j++)
977 if (DotProduct(new_velocity, planes[j]) < 0)
987 // go along this plane
988 VectorCopy(new_velocity, ent->fields.server->velocity);
992 // go along the crease
995 VectorClear(ent->fields.server->velocity);
999 CrossProduct(planes[0], planes[1], dir);
1000 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1001 VectorNormalize(dir);
1002 d = DotProduct(dir, ent->fields.server->velocity);
1003 VectorScale(dir, d, ent->fields.server->velocity);
1007 // if current velocity is against the original velocity,
1008 // stop dead to avoid tiny occilations in sloping corners
1009 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1011 VectorClear(ent->fields.server->velocity);
1016 //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]);
1019 if ((blocked & 1) == 0 && bumpcount > 1)
1021 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1022 // flag ONGROUND if there's ground under it
1023 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent, hitsupercontentsmask);
1027 // LordHavoc: this came from QW and allows you to get out of water more easily
1028 if (sv_gameplayfix_easierwaterjump.integer && ((int)ent->fields.server->flags & FL_WATERJUMP) && !(blocked & 8))
1029 VectorCopy(primal_velocity, ent->fields.server->velocity);
1030 if (applygravity && !((int)ent->fields.server->flags & FL_ONGROUND))
1031 ent->fields.server->velocity[2] -= gravity;
1041 static float SV_Gravity (prvm_edict_t *ent)
1046 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1047 if (val!=0 && val->_float)
1048 ent_gravity = val->_float;
1051 return ent_gravity * sv_gravity.value * sv.frametime;
1056 ===============================================================================
1060 ===============================================================================
1067 Does not change the entities velocity at all
1068 The trace struct is filled with the trace that has been done.
1069 Returns true if the push did not result in the entity being teleported by QC code.
1072 static qboolean SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid, qboolean dolink)
1078 VectorAdd (ent->fields.server->origin, push, end);
1080 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1081 type = MOVE_MISSILE;
1082 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1083 type = MOVE_NOMONSTERS; // only clip against bmodels
1087 *trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent));
1088 if (trace->bmodelstartsolid && failonbmodelstartsolid)
1091 VectorCopy (trace->endpos, ent->fields.server->origin);
1094 if(!trace->startsolid)
1095 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)
1097 Con_Printf("something eeeeevil happened\n");
1101 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)));
1105 SV_LinkEdict (ent, dolink);
1106 return SV_Impact (ent, trace);
1109 SV_LinkEdict (ent, true);
1121 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1124 int pusherowner, pusherprog;
1127 float savesolid, movetime2, pushltime;
1128 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1130 int numcheckentities;
1131 static prvm_edict_t *checkentities[MAX_EDICTS];
1132 dp_model_t *pushermodel;
1133 trace_t trace, trace2;
1134 matrix4x4_t pusherfinalmatrix, pusherfinalimatrix;
1135 unsigned short moved_edicts[MAX_EDICTS];
1137 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])
1139 pusher->fields.server->ltime += movetime;
1143 switch ((int) pusher->fields.server->solid)
1145 // LordHavoc: valid pusher types
1148 case SOLID_SLIDEBOX:
1149 case SOLID_CORPSE: // LordHavoc: this would be weird...
1151 // LordHavoc: no collisions
1154 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1155 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1156 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1157 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1158 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1159 pusher->fields.server->ltime += movetime;
1160 SV_LinkEdict (pusher, false);
1163 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1166 index = (int) pusher->fields.server->modelindex;
1167 if (index < 1 || index >= MAX_MODELS)
1169 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1172 pushermodel = sv.models[index];
1173 pusherowner = pusher->fields.server->owner;
1174 pusherprog = PRVM_EDICT_TO_PROG(pusher);
1176 rotated = VectorLength2(pusher->fields.server->angles) + VectorLength2(pusher->fields.server->avelocity) > 0;
1178 movetime2 = movetime;
1179 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1180 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1181 if (moveangle[0] || moveangle[2])
1183 for (i = 0;i < 3;i++)
1187 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1188 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1192 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1193 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1197 else if (moveangle[1])
1199 for (i = 0;i < 3;i++)
1203 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1204 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1208 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1209 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1215 for (i = 0;i < 3;i++)
1219 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1220 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1224 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1225 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1230 VectorNegate (moveangle, a);
1231 AngleVectorsFLU (a, forward, left, up);
1233 VectorCopy (pusher->fields.server->origin, pushorig);
1234 VectorCopy (pusher->fields.server->angles, pushang);
1235 pushltime = pusher->fields.server->ltime;
1237 // move the pusher to its final position
1239 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1240 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1241 pusher->fields.server->ltime += movetime;
1242 SV_LinkEdict (pusher, false);
1245 if (pusher->fields.server->modelindex >= 1 && pusher->fields.server->modelindex < MAX_MODELS)
1246 pushermodel = sv.models[(int)pusher->fields.server->modelindex];
1247 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);
1248 Matrix4x4_Invert_Simple(&pusherfinalimatrix, &pusherfinalmatrix);
1250 savesolid = pusher->fields.server->solid;
1252 // see if any solid entities are inside the final position
1255 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1256 for (e = 0;e < numcheckentities;e++)
1258 prvm_edict_t *check = checkentities[e];
1259 if (check->fields.server->movetype == MOVETYPE_NONE
1260 || check->fields.server->movetype == MOVETYPE_PUSH
1261 || check->fields.server->movetype == MOVETYPE_FOLLOW
1262 || check->fields.server->movetype == MOVETYPE_NOCLIP
1263 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1266 if (check->fields.server->owner == pusherprog)
1269 if (pusherowner == PRVM_EDICT_TO_PROG(check))
1272 //Con_Printf("%i %s ", PRVM_NUM_FOR_EDICT(check), PRVM_GetString(check->fields.server->classname));
1274 // tell any MOVETYPE_STEP entity that it may need to check for water transitions
1275 check->priv.server->waterposition_forceupdate = true;
1277 checkcontents = SV_GenericHitSuperContentsMask(check);
1279 // if the entity is standing on the pusher, it will definitely be moved
1280 // if the entity is not standing on the pusher, but is in the pusher's
1281 // final position, move it
1282 if (!((int)check->fields.server->flags & FL_ONGROUND) || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher)
1284 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);
1285 //trace = SV_Move(check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, MOVE_NOMONSTERS, check, checkcontents);
1286 if (!trace.startsolid)
1288 //Con_Printf("- not in solid\n");
1296 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1297 org2[0] = DotProduct (org, forward);
1298 org2[1] = DotProduct (org, left);
1299 org2[2] = DotProduct (org, up);
1300 VectorSubtract (org2, org, move);
1301 VectorAdd (move, move1, move);
1304 VectorCopy (move1, move);
1306 //Con_Printf("- pushing %f %f %f\n", move[0], move[1], move[2]);
1308 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1309 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1310 moved_edicts[num_moved++] = PRVM_NUM_FOR_EDICT(check);
1312 // try moving the contacted entity
1313 pusher->fields.server->solid = SOLID_NOT;
1314 if(!SV_PushEntity (&trace, check, move, true, true))
1316 // entity "check" got teleported
1317 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1318 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1319 continue; // pushed enough
1321 // FIXME: turn players specially
1322 check->fields.server->angles[1] += trace.fraction * moveangle[1];
1323 pusher->fields.server->solid = savesolid; // was SOLID_BSP
1324 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
1326 // this trace.fraction < 1 check causes items to fall off of pushers
1327 // if they pass under or through a wall
1328 // the groundentity check causes items to fall off of ledges
1329 if (check->fields.server->movetype != MOVETYPE_WALK && (trace.fraction < 1 || PRVM_PROG_TO_EDICT(check->fields.server->groundentity) != pusher))
1330 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1332 // if it is still inside the pusher, block
1333 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);
1334 if (trace.startsolid)
1336 // try moving the contacted entity a tiny bit further to account for precision errors
1338 pusher->fields.server->solid = SOLID_NOT;
1339 VectorScale(move, 1.1, move2);
1340 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1341 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1342 if(!SV_PushEntity (&trace2, check, move2, true, true))
1344 // entity "check" got teleported
1347 pusher->fields.server->solid = savesolid;
1348 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);
1349 if (trace.startsolid)
1351 // try moving the contacted entity a tiny bit less to account for precision errors
1352 pusher->fields.server->solid = SOLID_NOT;
1353 VectorScale(move, 0.9, move2);
1354 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1355 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1356 if(!SV_PushEntity (&trace2, check, move2, true, true))
1358 // entity "check" got teleported
1361 pusher->fields.server->solid = savesolid;
1362 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);
1363 if (trace.startsolid)
1365 // still inside pusher, so it's really blocked
1368 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1370 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1373 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1374 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1378 VectorCopy (pushorig, pusher->fields.server->origin);
1379 VectorCopy (pushang, pusher->fields.server->angles);
1380 pusher->fields.server->ltime = pushltime;
1381 SV_LinkEdict (pusher, false);
1383 // move back any entities we already moved
1384 for (i = 0;i < num_moved;i++)
1386 prvm_edict_t *ed = PRVM_EDICT_NUM(moved_edicts[i]);
1387 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1388 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1389 SV_LinkEdict (ed, false);
1392 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1393 if (pusher->fields.server->blocked)
1395 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1396 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1397 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1404 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1405 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1406 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1415 void SV_Physics_Pusher (prvm_edict_t *ent)
1417 float thinktime, oldltime, movetime;
1419 oldltime = ent->fields.server->ltime;
1421 thinktime = ent->fields.server->nextthink;
1422 if (thinktime < ent->fields.server->ltime + sv.frametime)
1424 movetime = thinktime - ent->fields.server->ltime;
1429 movetime = sv.frametime;
1432 // advances ent->fields.server->ltime if not blocked
1433 SV_PushMove (ent, movetime);
1435 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1437 ent->fields.server->nextthink = 0;
1438 prog->globals.server->time = sv.time;
1439 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1440 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1441 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1447 ===============================================================================
1451 ===============================================================================
1454 static float unstickoffsets[] =
1456 // poutting -/+z changes first as they are least weird
1471 typedef enum unstickresult_e
1479 unstickresult_t SV_UnstickEntityReturnOffset (prvm_edict_t *ent, vec3_t offset)
1483 // if not stuck in a bmodel, just return
1484 if (!SV_TestEntityPosition(ent, vec3_origin))
1485 return UNSTICK_GOOD;
1487 for (i = 0;i < (int)(sizeof(unstickoffsets) / sizeof(unstickoffsets[0]));i += 3)
1489 if (!SV_TestEntityPosition(ent, unstickoffsets + i))
1491 VectorCopy(unstickoffsets + i, offset);
1492 SV_LinkEdict (ent, true);
1493 return UNSTICK_UNSTUCK;
1497 maxunstick = (int) ((ent->fields.server->maxs[2] - ent->fields.server->mins[2]) * 0.36);
1498 // magic number 0.36 allows unsticking by up to 17 units with the largest supported bbox
1500 for(i = 2; i <= maxunstick; ++i)
1502 VectorClear(offset);
1504 if (!SV_TestEntityPosition(ent, offset))
1506 SV_LinkEdict (ent, true);
1507 return UNSTICK_UNSTUCK;
1510 if (!SV_TestEntityPosition(ent, offset))
1512 SV_LinkEdict (ent, true);
1513 return UNSTICK_UNSTUCK;
1517 return UNSTICK_STUCK;
1520 qboolean SV_UnstickEntity (prvm_edict_t *ent)
1523 switch(SV_UnstickEntityReturnOffset(ent, offset))
1527 case UNSTICK_UNSTUCK:
1528 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]);
1531 if (developer.integer >= 100)
1532 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1535 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1544 This is a big hack to try and fix the rare case of getting stuck in the world
1548 void SV_CheckStuck (prvm_edict_t *ent)
1552 switch(SV_UnstickEntityReturnOffset(ent, offset))
1555 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1557 case UNSTICK_UNSTUCK:
1558 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]);
1561 VectorSubtract(ent->fields.server->oldorigin, ent->fields.server->origin, offset);
1562 if (!SV_TestEntityPosition(ent, offset))
1564 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1565 SV_LinkEdict (ent, true);
1568 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1571 Con_Printf("SV_UnstickEntityReturnOffset returned a value outside its enum.\n");
1581 qboolean SV_CheckWater (prvm_edict_t *ent)
1584 int nNativeContents;
1587 point[0] = ent->fields.server->origin[0];
1588 point[1] = ent->fields.server->origin[1];
1589 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1591 // DRESK - Support for Entity Contents Transition Event
1592 // NOTE: Some logic needed to be slightly re-ordered
1593 // to not affect performance and allow for the feature.
1595 // Acquire Super Contents Prior to Resets
1596 cont = SV_PointSuperContents(point);
1597 // Acquire Native Contents Here
1598 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1600 // DRESK - Support for Entity Contents Transition Event
1601 if(ent->fields.server->watertype)
1602 // Entity did NOT Spawn; Check
1603 SV_CheckContentsTransition(ent, nNativeContents);
1606 ent->fields.server->waterlevel = 0;
1607 ent->fields.server->watertype = CONTENTS_EMPTY;
1608 cont = SV_PointSuperContents(point);
1609 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1611 ent->fields.server->watertype = nNativeContents;
1612 ent->fields.server->waterlevel = 1;
1613 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1614 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1616 ent->fields.server->waterlevel = 2;
1617 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1618 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1619 ent->fields.server->waterlevel = 3;
1623 return ent->fields.server->waterlevel > 1;
1632 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1635 vec3_t forward, into, side;
1637 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1638 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1640 // cut the tangential velocity
1641 i = DotProduct (stepnormal, ent->fields.server->velocity);
1642 VectorScale (stepnormal, i, into);
1643 VectorSubtract (ent->fields.server->velocity, into, side);
1644 ent->fields.server->velocity[0] = side[0] * (1 + d);
1645 ent->fields.server->velocity[1] = side[1] * (1 + d);
1651 =====================
1654 Player has come to a dead stop, possibly due to the problem with limited
1655 float precision at some angle joins in the BSP hull.
1657 Try fixing by pushing one pixel in each direction.
1659 This is a hack, but in the interest of good gameplay...
1660 ======================
1662 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1667 VectorCopy (ent->fields.server->origin, oldorg);
1670 for (i=0 ; i<8 ; i++)
1672 // try pushing a little in an axial direction
1675 case 0: dir[0] = 2; dir[1] = 0; break;
1676 case 1: dir[0] = 0; dir[1] = 2; break;
1677 case 2: dir[0] = -2; dir[1] = 0; break;
1678 case 3: dir[0] = 0; dir[1] = -2; break;
1679 case 4: dir[0] = 2; dir[1] = 2; break;
1680 case 5: dir[0] = -2; dir[1] = 2; break;
1681 case 6: dir[0] = 2; dir[1] = -2; break;
1682 case 7: dir[0] = -2; dir[1] = -2; break;
1685 SV_PushEntity (&trace, ent, dir, false, true);
1687 // retry the original move
1688 ent->fields.server->velocity[0] = oldvel[0];
1689 ent->fields.server->velocity[1] = oldvel[1];
1690 ent->fields.server->velocity[2] = 0;
1691 clip = SV_FlyMove (ent, 0.1, NULL, SV_GenericHitSuperContentsMask(ent));
1693 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1694 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1696 Con_DPrint("TryUnstick - success.\n");
1700 // go back to the original pos and try again
1701 VectorCopy (oldorg, ent->fields.server->origin);
1705 VectorClear (ent->fields.server->velocity);
1706 Con_DPrint("TryUnstick - failure.\n");
1712 =====================
1715 Only used by players
1716 ======================
1718 void SV_WalkMove (prvm_edict_t *ent)
1720 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity, hitsupercontentsmask;
1721 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1722 trace_t downtrace, trace;
1723 qboolean applygravity;
1725 // if frametime is 0 (due to client sending the same timestamp twice),
1727 if (sv.frametime <= 0)
1730 SV_CheckStuck (ent);
1732 applygravity = !SV_CheckWater (ent) && ent->fields.server->movetype == MOVETYPE_WALK && ! ((int)ent->fields.server->flags & FL_WATERJUMP);
1734 hitsupercontentsmask = SV_GenericHitSuperContentsMask(ent);
1736 SV_CheckVelocity(ent);
1738 // do a regular slide move unless it looks like you ran into a step
1739 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1741 VectorCopy (ent->fields.server->origin, start_origin);
1742 VectorCopy (ent->fields.server->velocity, start_velocity);
1744 clip = SV_FlyMove (ent, sv.frametime, applygravity, NULL, hitsupercontentsmask);
1746 // if the move did not hit the ground at any point, we're not on ground
1748 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1750 SV_CheckVelocity(ent);
1751 SV_LinkEdict (ent, true);
1753 if(clip & 8) // teleport
1756 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1759 if (sv_nostep.integer)
1762 VectorCopy(ent->fields.server->origin, originalmove_origin);
1763 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1764 originalmove_clip = clip;
1765 originalmove_flags = (int)ent->fields.server->flags;
1766 originalmove_groundentity = ent->fields.server->groundentity;
1768 // if move didn't block on a step, return
1771 // if move was not trying to move into the step, return
1772 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1775 if (ent->fields.server->movetype != MOVETYPE_FLY)
1777 // return if gibbed by a trigger
1778 if (ent->fields.server->movetype != MOVETYPE_WALK)
1781 // only step up while jumping if that is enabled
1782 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1783 if (!oldonground && ent->fields.server->waterlevel == 0)
1787 // try moving up and forward to go up a step
1788 // back to start pos
1789 VectorCopy (start_origin, ent->fields.server->origin);
1790 VectorCopy (start_velocity, ent->fields.server->velocity);
1793 VectorClear (upmove);
1794 upmove[2] = sv_stepheight.value;
1795 if(!SV_PushEntity(&trace, ent, upmove, false, true))
1797 // we got teleported when upstepping... must abort the move
1802 ent->fields.server->velocity[2] = 0;
1803 clip = SV_FlyMove (ent, sv.frametime, applygravity, stepnormal, hitsupercontentsmask);
1804 ent->fields.server->velocity[2] += start_velocity[2];
1807 // we got teleported when upstepping... must abort the move
1808 // note that z velocity handling may not be what QC expects here, but we cannot help it
1812 SV_CheckVelocity(ent);
1813 SV_LinkEdict (ent, true);
1815 // check for stuckness, possibly due to the limited precision of floats
1816 // in the clipping hulls
1818 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1819 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1821 //Con_Printf("wall\n");
1822 // stepping up didn't make any progress, revert to original move
1823 VectorCopy(originalmove_origin, ent->fields.server->origin);
1824 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1825 //clip = originalmove_clip;
1826 ent->fields.server->flags = originalmove_flags;
1827 ent->fields.server->groundentity = originalmove_groundentity;
1828 // now try to unstick if needed
1829 //clip = SV_TryUnstick (ent, oldvel);
1833 //Con_Printf("step - ");
1835 // extra friction based on view angle
1836 if (clip & 2 && sv_wallfriction.integer)
1837 SV_WallFriction (ent, stepnormal);
1839 // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground
1840 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))
1844 VectorClear (downmove);
1845 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1846 if(!SV_PushEntity (&downtrace, ent, downmove, false, true))
1848 // we got teleported when downstepping... must abort the move
1852 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1854 // this has been disabled so that you can't jump when you are stepping
1855 // up while already jumping (also known as the Quake2 double jump bug)
1857 // LordHavoc: disabled this check so you can walk on monsters/players
1858 //if (ent->fields.server->solid == SOLID_BSP)
1860 //Con_Printf("onground\n");
1861 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1862 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1868 //Con_Printf("slope\n");
1869 // if the push down didn't end up on good ground, use the move without
1870 // the step up. This happens near wall / slope combinations, and can
1871 // cause the player to hop up higher on a slope too steep to climb
1872 VectorCopy(originalmove_origin, ent->fields.server->origin);
1873 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1874 //clip = originalmove_clip;
1875 ent->fields.server->flags = originalmove_flags;
1876 ent->fields.server->groundentity = originalmove_groundentity;
1879 SV_CheckVelocity(ent);
1880 SV_LinkEdict (ent, true);
1883 //============================================================================
1889 Entities that are "stuck" to another entity
1892 void SV_Physics_Follow (prvm_edict_t *ent)
1894 vec3_t vf, vr, vu, angles, v;
1898 if (!SV_RunThink (ent))
1901 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1902 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1903 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])
1905 // quick case for no rotation
1906 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1910 angles[0] = -ent->fields.server->punchangle[0];
1911 angles[1] = ent->fields.server->punchangle[1];
1912 angles[2] = ent->fields.server->punchangle[2];
1913 AngleVectors (angles, vf, vr, vu);
1914 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];
1915 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];
1916 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];
1917 angles[0] = -e->fields.server->angles[0];
1918 angles[1] = e->fields.server->angles[1];
1919 angles[2] = e->fields.server->angles[2];
1920 AngleVectors (angles, vf, vr, vu);
1921 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1922 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1923 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1925 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1926 SV_LinkEdict (ent, true);
1930 ==============================================================================
1934 ==============================================================================
1939 SV_CheckWaterTransition
1943 void SV_CheckWaterTransition (prvm_edict_t *ent)
1946 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1947 if (!ent->fields.server->watertype)
1949 // just spawned here
1950 ent->fields.server->watertype = cont;
1951 ent->fields.server->waterlevel = 1;
1955 // DRESK - Support for Entity Contents Transition Event
1956 // NOTE: Call here BEFORE updating the watertype below,
1957 // and suppress watersplash sound if a valid function
1958 // call was made to allow for custom "splash" sounds.
1959 if( !SV_CheckContentsTransition(ent, cont) )
1960 { // Contents Transition Function Invalid; Potentially Play Water Sound
1961 // check if the entity crossed into or out of water
1962 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1963 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1966 if (cont <= CONTENTS_WATER)
1968 ent->fields.server->watertype = cont;
1969 ent->fields.server->waterlevel = 1;
1973 ent->fields.server->watertype = CONTENTS_EMPTY;
1974 ent->fields.server->waterlevel = 0;
1982 Toss, bounce, and fly movement. When onground, do nothing.
1985 void SV_Physics_Toss (prvm_edict_t *ent)
1992 // if onground, return without moving
1993 if ((int)ent->fields.server->flags & FL_ONGROUND)
1995 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1997 // don't stick to ground if onground and moving upward
1998 ent->fields.server->flags -= FL_ONGROUND;
2000 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
2002 // we can trust FL_ONGROUND if groundentity is world because it never moves
2005 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
2007 // if ent was supported by a brush model on previous frame,
2008 // and groundentity is now freed, set groundentity to 0 (world)
2009 // which leaves it suspended in the air
2010 ent->fields.server->groundentity = 0;
2014 ent->priv.server->suspendedinairflag = false;
2016 SV_CheckVelocity (ent);
2019 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
2020 ent->fields.server->velocity[2] -= SV_Gravity(ent);
2023 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2025 movetime = sv.frametime;
2026 for (bump = 0;bump < MAX_CLIP_PLANES && movetime > 0;bump++)
2029 VectorScale (ent->fields.server->velocity, movetime, move);
2030 if(!SV_PushEntity (&trace, ent, move, true, true))
2031 return; // teleported
2032 if (ent->priv.server->free)
2034 if (trace.bmodelstartsolid)
2036 // try to unstick the entity
2037 SV_UnstickEntity(ent);
2038 if(!SV_PushEntity (&trace, ent, move, false, true))
2039 return; // teleported
2040 if (ent->priv.server->free)
2043 if (trace.fraction == 1)
2045 movetime *= 1 - min(1, trace.fraction);
2046 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
2048 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
2049 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2051 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
2053 float d, ent_gravity;
2055 float bouncefactor = 0.5f;
2056 float bouncestop = 60.0f / 800.0f;
2058 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncefactor);
2059 if (val!=0 && val->_float)
2060 bouncefactor = val->_float;
2062 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.bouncestop);
2063 if (val!=0 && val->_float)
2064 bouncestop = val->_float;
2066 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1 + bouncefactor);
2067 // LordHavoc: fixed grenades not bouncing when fired down a slope
2068 val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
2069 if (val!=0 && val->_float)
2070 ent_gravity = val->_float;
2073 if (sv_gameplayfix_grenadebouncedownslopes.integer)
2075 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
2076 if (trace.plane.normal[2] > 0.7 && fabs(d) < sv_gravity.value * bouncestop * ent_gravity)
2078 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2079 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2080 VectorClear (ent->fields.server->velocity);
2081 VectorClear (ent->fields.server->avelocity);
2084 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2088 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < sv_gravity.value * bouncestop * ent_gravity)
2090 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2091 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2092 VectorClear (ent->fields.server->velocity);
2093 VectorClear (ent->fields.server->avelocity);
2096 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2101 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
2102 if (trace.plane.normal[2] > 0.7)
2104 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
2105 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
2106 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
2107 ent->priv.server->suspendedinairflag = true;
2108 VectorClear (ent->fields.server->velocity);
2109 VectorClear (ent->fields.server->avelocity);
2112 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2114 if (!sv_gameplayfix_slidemoveprojectiles.integer || (ent->fields.server->movetype != MOVETYPE_BOUNCE && ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE) || ((int)ent->fields.server->flags & FL_ONGROUND))
2118 // check for in water
2119 SV_CheckWaterTransition (ent);
2123 ===============================================================================
2127 ===============================================================================
2134 Monsters freefall when they don't have a ground entity, otherwise
2135 all movement is done with discrete steps.
2137 This is also used for objects that have become still on the ground, but
2138 will fall if the floor is pulled out from under them.
2141 void SV_Physics_Step (prvm_edict_t *ent)
2143 int flags = (int)ent->fields.server->flags;
2146 // Backup Velocity in the event that movetypesteplandevent is called,
2147 // to provide a parameter with the entity's velocity at impact.
2148 prvm_eval_t *movetypesteplandevent;
2149 vec3_t backupVelocity;
2150 VectorCopy(ent->fields.server->velocity, backupVelocity);
2151 // don't fall at all if fly/swim
2152 if (!(flags & (FL_FLY | FL_SWIM)))
2154 if (flags & FL_ONGROUND)
2156 // freefall if onground and moving upward
2157 // freefall if not standing on a world surface (it may be a lift or trap door)
2158 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2160 ent->fields.server->flags -= FL_ONGROUND;
2161 SV_CheckVelocity(ent);
2162 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2163 SV_LinkEdict(ent, true);
2164 ent->priv.server->waterposition_forceupdate = true;
2169 // freefall if not onground
2170 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2172 SV_CheckVelocity(ent);
2173 SV_FlyMove(ent, sv.frametime, true, NULL, SV_GenericHitSuperContentsMask(ent));
2174 SV_LinkEdict(ent, true);
2177 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND)
2179 // DRESK - Check for Entity Land Event Function
2180 movetypesteplandevent = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.movetypesteplandevent);
2182 if(movetypesteplandevent->function)
2183 { // Valid Function; Execute
2184 // Prepare Parameters
2185 // Assign Velocity at Impact
2186 PRVM_G_VECTOR(OFS_PARM0)[0] = backupVelocity[0];
2187 PRVM_G_VECTOR(OFS_PARM0)[1] = backupVelocity[1];
2188 PRVM_G_VECTOR(OFS_PARM0)[2] = backupVelocity[2];
2190 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2191 // Execute VM Function
2192 PRVM_ExecuteProgram(movetypesteplandevent->function, "movetypesteplandevent: NULL function");
2195 // Check for Engine Landing Sound
2196 if(sv_sound_land.string)
2197 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2199 ent->priv.server->waterposition_forceupdate = true;
2204 if (!SV_RunThink(ent))
2207 if (ent->priv.server->waterposition_forceupdate || !VectorCompare(ent->fields.server->origin, ent->priv.server->waterposition_origin))
2209 ent->priv.server->waterposition_forceupdate = false;
2210 VectorCopy(ent->fields.server->origin, ent->priv.server->waterposition_origin);
2211 SV_CheckWaterTransition(ent);
2215 //============================================================================
2217 static void SV_Physics_Entity (prvm_edict_t *ent)
2219 // don't run think/move on newly spawned projectiles as it messes up
2220 // movement interpolation and rocket trails, and is inconsistent with
2221 // respect to entities spawned in the same frame
2222 // (if an ent spawns a higher numbered ent, it moves in the same frame,
2223 // but if it spawns a lower numbered ent, it doesn't - this never moves
2224 // ents in the first frame regardless)
2225 qboolean runmove = ent->priv.server->move;
2226 ent->priv.server->move = true;
2227 if (!runmove && sv_gameplayfix_delayprojectiles.integer)
2229 switch ((int) ent->fields.server->movetype)
2232 case MOVETYPE_FAKEPUSH:
2233 SV_Physics_Pusher (ent);
2236 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2237 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2240 case MOVETYPE_FOLLOW:
2241 SV_Physics_Follow (ent);
2243 case MOVETYPE_NOCLIP:
2244 if (SV_RunThink(ent))
2247 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2248 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2250 SV_LinkEdict(ent, false);
2253 SV_Physics_Step (ent);
2256 if (SV_RunThink (ent))
2260 case MOVETYPE_BOUNCE:
2261 case MOVETYPE_BOUNCEMISSILE:
2262 case MOVETYPE_FLYMISSILE:
2265 if (SV_RunThink (ent))
2266 SV_Physics_Toss (ent);
2269 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2274 void SV_Physics_ClientMove(void)
2277 ent = host_client->edict;
2279 // call player physics, this needs the proper frametime
2280 prog->globals.server->frametime = sv.frametime;
2283 // call standard client pre-think, with frametime = 0
2284 prog->globals.server->time = sv.time;
2285 prog->globals.server->frametime = 0;
2286 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2287 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2288 prog->globals.server->frametime = sv.frametime;
2290 // make sure the velocity is sane (not a NaN)
2291 SV_CheckVelocity(ent);
2292 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2293 // player_run/player_stand1 does not horribly malfunction if the
2294 // velocity becomes a number that is both == 0 and != 0
2295 // (sounds to me like NaN but to be absolutely safe...)
2296 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2297 VectorClear(ent->fields.server->velocity);
2299 // perform MOVETYPE_WALK behavior
2302 // call standard player post-think, with frametime = 0
2303 prog->globals.server->time = sv.time;
2304 prog->globals.server->frametime = 0;
2305 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2306 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2307 prog->globals.server->frametime = sv.frametime;
2309 if(ent->fields.server->fixangle)
2311 // angle fixing was requested by physics code...
2312 // so store the current angles for later use
2313 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2314 host_client->fixangle_angles_set = TRUE;
2316 // and clear fixangle for the next frame
2317 ent->fields.server->fixangle = 0;
2321 void SV_Physics_ClientEntity(prvm_edict_t *ent)
2323 // don't do physics on disconnected clients, FrikBot relies on this
2324 if (!host_client->spawned)
2326 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2330 // don't run physics here if running asynchronously
2331 if (host_client->clmovement_inputtimeout <= 0)
2334 //host_client->cmd.time = max(host_client->cmd.time, sv.time);
2337 // make sure the velocity is sane (not a NaN)
2338 SV_CheckVelocity(ent);
2339 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2340 // player_run/player_stand1 does not horribly malfunction if the
2341 // velocity becomes a number that is both == 0 and != 0
2342 // (sounds to me like NaN but to be absolutely safe...)
2343 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2344 VectorClear(ent->fields.server->velocity);
2346 // call standard client pre-think
2347 prog->globals.server->time = sv.time;
2348 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2349 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2350 SV_CheckVelocity (ent);
2352 switch ((int) ent->fields.server->movetype)
2355 case MOVETYPE_FAKEPUSH:
2356 SV_Physics_Pusher (ent);
2359 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2360 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2363 case MOVETYPE_FOLLOW:
2364 SV_Physics_Follow (ent);
2366 case MOVETYPE_NOCLIP:
2369 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2370 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2373 SV_Physics_Step (ent);
2377 // don't run physics here if running asynchronously
2378 if (host_client->clmovement_inputtimeout <= 0)
2382 case MOVETYPE_BOUNCE:
2383 case MOVETYPE_BOUNCEMISSILE:
2384 case MOVETYPE_FLYMISSILE:
2387 SV_Physics_Toss (ent);
2394 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2398 // decrement the countdown variable used to decide when to go back to
2399 // synchronous physics
2400 if (host_client->clmovement_inputtimeout > sv.frametime)
2401 host_client->clmovement_inputtimeout -= sv.frametime;
2403 host_client->clmovement_inputtimeout = 0;
2405 SV_CheckVelocity (ent);
2407 SV_LinkEdict (ent, true);
2409 SV_CheckVelocity (ent);
2411 // call standard player post-think
2412 prog->globals.server->time = sv.time;
2413 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2414 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2416 if(ent->fields.server->fixangle)
2418 // angle fixing was requested by physics code...
2419 // so store the current angles for later use
2420 memcpy(host_client->fixangle_angles, ent->fields.server->angles, sizeof(host_client->fixangle_angles));
2421 host_client->fixangle_angles_set = TRUE;
2423 // and clear fixangle for the next frame
2424 ent->fields.server->fixangle = 0;
2434 void SV_Physics (void)
2439 // let the progs know that a new frame has started
2440 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2441 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2442 prog->globals.server->time = sv.time;
2443 prog->globals.server->frametime = sv.frametime;
2444 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2447 // treat each object in turn
2450 // if force_retouch, relink all the entities
2451 if (prog->globals.server->force_retouch > 0)
2452 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2453 if (!ent->priv.server->free)
2454 SV_LinkEdict (ent, true); // force retouch even for stationary
2456 // run physics on the client entities
2457 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2458 if (!ent->priv.server->free)
2459 SV_Physics_ClientEntity(ent);
2461 // run physics on all the non-client entities
2462 if (!sv_freezenonclients.integer)
2463 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2464 if (!ent->priv.server->free)
2465 SV_Physics_Entity(ent);
2467 if (prog->globals.server->force_retouch > 0)
2468 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2470 // LordHavoc: endframe support
2471 if (prog->funcoffsets.EndFrame)
2473 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2474 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2475 prog->globals.server->time = sv.time;
2476 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2479 // decrement prog->num_edicts if the highest number entities died
2480 for (;PRVM_ED_CanAlloc(PRVM_EDICT_NUM(prog->num_edicts - 1));prog->num_edicts--);
2482 if (!sv_freezenonclients.integer)
2483 sv.time += sv.frametime;