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 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
43 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
44 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
45 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
46 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
47 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
48 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
49 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
50 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
51 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
52 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
53 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
54 cvar_t sv_debugmove = {CVAR_NOTIFY, "sv_debugmove", "0", "disables collision detection optimizations for debugging purposes"};
56 cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
57 cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
59 // TODO: move this extern to server.h
60 extern cvar_t sv_clmovement_waitforinput;
62 #define MOVE_EPSILON 0.01
64 void SV_Physics_Toss (prvm_edict_t *ent);
66 void SV_Phys_Init (void)
68 Cvar_RegisterVariable(&sv_stepheight);
69 Cvar_RegisterVariable(&sv_jumpstep);
70 Cvar_RegisterVariable(&sv_wallfriction);
71 Cvar_RegisterVariable(&sv_newflymove);
72 Cvar_RegisterVariable(&sv_freezenonclients);
73 Cvar_RegisterVariable(&sv_playerphysicsqc);
74 Cvar_RegisterVariable(&sv_debugmove);
76 Cvar_RegisterVariable(&sv_sound_watersplash);
77 Cvar_RegisterVariable(&sv_sound_land);
81 ===============================================================================
85 ===============================================================================
92 Handles selection or creation of a clipping hull, and offseting (and
93 eventually rotation) of the end points
96 trace_t SV_Move_ClipToEntity(prvm_edict_t *ent, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int movetype, int hitsupercontents)
99 model_t *model = NULL;
100 matrix4x4_t matrix, imatrix;
101 float tempnormal[3], starttransformed[3], endtransformed[3];
103 memset(&trace, 0, sizeof(trace));
104 trace.fraction = trace.realfraction = 1;
105 VectorCopy(end, trace.endpos);
107 if ((int) ent->fields.server->solid == SOLID_BSP || movetype == MOVE_HITMODEL)
109 unsigned int modelindex = (unsigned int)ent->fields.server->modelindex;
110 // if the modelindex is 0, it shouldn't be SOLID_BSP!
113 Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with no model\n", PRVM_NUM_FOR_EDICT(ent));
116 if (modelindex >= MAX_MODELS)
118 Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent));
121 model = sv.models[modelindex];
122 if (modelindex != 0 && model == NULL)
124 Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent));
128 if ((int) ent->fields.server->solid == SOLID_BSP)
130 if (!model->TraceBox)
132 Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP with a non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
135 //if ((int) ent->fields.server->movetype != MOVETYPE_PUSH)
137 // Con_Printf("SV_Move_ClipToEntity: edict %i: SOLID_BSP without MOVETYPE_PUSH\n", PRVM_NUM_FOR_EDICT(ent));
141 Matrix4x4_CreateFromQuakeEntity(&matrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2], ent->fields.server->angles[0], ent->fields.server->angles[1], ent->fields.server->angles[2], 1);
144 Matrix4x4_CreateTranslate(&matrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2]);
146 Matrix4x4_Invert_Simple(&imatrix, &matrix);
147 Matrix4x4_Transform(&imatrix, start, starttransformed);
148 Matrix4x4_Transform(&imatrix, end, endtransformed);
149 #if COLLISIONPARANOID >= 3
150 Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]);
153 if (model && model->TraceBox)
156 frame = (int)ent->fields.server->frame;
157 frame = bound(0, frame, (model->numframes - 1));
158 model->TraceBox(model, frame, &trace, starttransformed, mins, maxs, endtransformed, hitsupercontents);
161 Collision_ClipTrace_Box(&trace, ent->fields.server->mins, ent->fields.server->maxs, starttransformed, mins, maxs, endtransformed, hitsupercontents, ent->fields.server->solid == SOLID_CORPSE ? SUPERCONTENTS_CORPSE : SUPERCONTENTS_BODY, 0, NULL);
162 trace.fraction = bound(0, trace.fraction, 1);
163 trace.realfraction = bound(0, trace.realfraction, 1);
165 if (trace.fraction < 1)
167 VectorLerp(start, trace.fraction, end, trace.endpos);
168 VectorCopy(trace.plane.normal, tempnormal);
169 Matrix4x4_Transform3x3(&matrix, tempnormal, trace.plane.normal);
170 // FIXME: should recalc trace.plane.dist
173 VectorCopy(end, trace.endpos);
183 trace_t SV_Move_ClipToWorld(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int movetype, int hitsupercontents)
186 memset(&trace, 0, sizeof(trace));
187 trace.fraction = trace.realfraction = 1;
188 sv.worldmodel->TraceBox(sv.worldmodel, 0, &trace, start, mins, maxs, end, hitsupercontents);
189 trace.fraction = bound(0, trace.fraction, 1);
190 trace.realfraction = bound(0, trace.realfraction, 1);
191 VectorLerp(start, trace.fraction, end, trace.endpos);
200 #if COLLISIONPARANOID >= 1
201 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)
203 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)
206 vec3_t hullmins, hullmaxs;
208 int hitsupercontentsmask;
211 prvm_edict_t *traceowner, *touch;
214 // bounding box of entire move area
215 vec3_t clipboxmins, clipboxmaxs;
216 // size of the moving object
217 vec3_t clipmins, clipmaxs;
218 // size when clipping against monsters
219 vec3_t clipmins2, clipmaxs2;
220 // start and end origin of move
221 vec3_t clipstart, clipend;
225 prvm_edict_t *touchedicts[MAX_EDICTS];
227 VectorCopy(start, clipstart);
228 VectorCopy(end, clipend);
229 VectorCopy(mins, clipmins);
230 VectorCopy(maxs, clipmaxs);
231 VectorCopy(mins, clipmins2);
232 VectorCopy(maxs, clipmaxs2);
233 #if COLLISIONPARANOID >= 3
234 Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]);
239 val = PRVM_GETEDICTFIELDVALUE(passedict, prog->fieldoffsets.dphitcontentsmask);
240 if (val && val->_float)
241 hitsupercontentsmask = (int)val->_float;
242 else if (passedict->fields.server->solid == SOLID_SLIDEBOX)
244 if ((int)passedict->fields.server->flags & FL_MONSTER)
245 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_MONSTERCLIP;
247 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP;
249 else if (passedict->fields.server->solid == SOLID_CORPSE)
250 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY;
252 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
255 hitsupercontentsmask = SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_CORPSE;
258 cliptrace = SV_Move_ClipToWorld(clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask);
259 cliptrace.bmodelstartsolid = cliptrace.startsolid;
260 if (cliptrace.startsolid || cliptrace.fraction < 1)
261 cliptrace.ent = prog->edicts;
262 if (type == MOVE_WORLDONLY)
265 if (type == MOVE_MISSILE)
267 // LordHavoc: modified this, was = -15, now -= 15
268 for (i = 0;i < 3;i++)
275 // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp
276 if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize)
277 sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs);
280 VectorCopy(clipmins, hullmins);
281 VectorCopy(clipmaxs, hullmaxs);
284 // create the bounding box of the entire move
285 for (i = 0;i < 3;i++)
287 clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1;
288 clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1;
291 // debug override to test against everything
292 if (sv_debugmove.integer)
294 clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999;
295 clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999;
298 // if the passedict is world, make it NULL (to avoid two checks each time)
299 if (passedict == prog->edicts)
301 // precalculate prog value for passedict for comparisons
302 passedictprog = PRVM_EDICT_TO_PROG(passedict);
303 // figure out whether this is a point trace for comparisons
304 pointtrace = VectorCompare(clipmins, clipmaxs);
305 // precalculate passedict's owner edict pointer for comparisons
306 traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.server->owner) : 0;
309 numtouchedicts = World_EntitiesInBox(&sv.world, clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts);
310 if (numtouchedicts > MAX_EDICTS)
312 // this never happens
313 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
314 numtouchedicts = MAX_EDICTS;
316 for (i = 0;i < numtouchedicts;i++)
318 touch = touchedicts[i];
320 if (touch->fields.server->solid < SOLID_BBOX)
322 if (type == MOVE_NOMONSTERS && touch->fields.server->solid != SOLID_BSP)
327 // don't clip against self
328 if (passedict == touch)
330 // don't clip owned entities against owner
331 if (traceowner == touch)
333 // don't clip owner against owned entities
334 if (passedictprog == touch->fields.server->owner)
336 // don't clip points against points (they can't collide)
337 if (pointtrace && VectorCompare(touch->fields.server->mins, touch->fields.server->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.server->flags & FL_MONSTER)))
341 // might interact, so do an exact clip
342 if ((int)touch->fields.server->flags & FL_MONSTER)
343 trace = SV_Move_ClipToEntity(touch, clipstart, clipmins2, clipmaxs2, clipend, type, hitsupercontentsmask);
345 trace = SV_Move_ClipToEntity(touch, clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask);
346 // LordHavoc: take the 'best' answers from the new trace and combine with existing data
348 cliptrace.allsolid = true;
349 if (trace.startsolid)
351 if (touch->fields.server->solid == SOLID_BSP)
352 cliptrace.bmodelstartsolid = true;
353 cliptrace.startsolid = true;
354 if (cliptrace.realfraction == 1)
355 cliptrace.ent = touch;
357 // don't set this except on the world, because it can easily confuse
358 // monsters underwater if there's a bmodel involved in the trace
359 // (inopen && inwater is how they check water visibility)
361 // cliptrace.inopen = true;
363 cliptrace.inwater = true;
364 if (trace.realfraction < cliptrace.realfraction)
366 cliptrace.fraction = trace.fraction;
367 cliptrace.realfraction = trace.realfraction;
368 VectorCopy(trace.endpos, cliptrace.endpos);
369 cliptrace.plane = trace.plane;
370 cliptrace.ent = touch;
371 cliptrace.hitsupercontents = trace.hitsupercontents;
372 cliptrace.hitq3surfaceflags = trace.hitq3surfaceflags;
373 cliptrace.hittexture = trace.hittexture;
375 cliptrace.startsupercontents |= trace.startsupercontents;
381 #if COLLISIONPARANOID >= 1
382 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)
387 trace = SV_Move_(start, mins, maxs, end, type, passedict);
390 VectorCopy(trace.endpos, temp);
391 endstuck = SV_Move_(temp, mins, maxs, temp, type, passedict).startsolid;
392 #if COLLISIONPARANOID < 3
393 if (trace.startsolid || endstuck)
395 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" : "");
402 ===============================================================================
404 Linking entities into the world culling system
406 ===============================================================================
409 void SV_LinkEdict_TouchAreaGrid(prvm_edict_t *ent)
411 int i, numtouchedicts, old_self, old_other;
412 prvm_edict_t *touch, *touchedicts[MAX_EDICTS];
414 // build a list of edicts to touch, because the link loop can be corrupted
415 // by SV_IncreaseEdicts called during touch functions
416 numtouchedicts = World_EntitiesInBox(&sv.world, ent->priv.server->areamins, ent->priv.server->areamaxs, MAX_EDICTS, touchedicts);
417 if (numtouchedicts > MAX_EDICTS)
419 // this never happens
420 Con_Printf("SV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS);
421 numtouchedicts = MAX_EDICTS;
424 old_self = prog->globals.server->self;
425 old_other = prog->globals.server->other;
426 for (i = 0;i < numtouchedicts;i++)
428 touch = touchedicts[i];
429 if (touch != ent && (int)touch->fields.server->solid == SOLID_TRIGGER && touch->fields.server->touch)
432 prog->globals.server->self = PRVM_EDICT_TO_PROG(touch);
433 prog->globals.server->other = PRVM_EDICT_TO_PROG(ent);
434 prog->globals.server->time = sv.time;
435 prog->globals.server->trace_allsolid = false;
436 prog->globals.server->trace_startsolid = false;
437 prog->globals.server->trace_fraction = 1;
438 prog->globals.server->trace_inwater = false;
439 prog->globals.server->trace_inopen = true;
440 VectorCopy (touch->fields.server->origin, prog->globals.server->trace_endpos);
441 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
442 prog->globals.server->trace_plane_dist = 0;
443 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(ent);
444 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
446 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
448 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
450 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
452 PRVM_ExecuteProgram (touch->fields.server->touch, "QC function self.touch is missing");
455 prog->globals.server->self = old_self;
456 prog->globals.server->other = old_other;
465 void SV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers)
470 if (ent == prog->edicts)
471 return; // don't add the world
473 if (ent->priv.server->free)
478 if (ent->fields.server->solid == SOLID_BSP)
480 int modelindex = (int)ent->fields.server->modelindex;
481 if (modelindex < 0 || modelindex > MAX_MODELS)
483 Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent));
486 model = sv.models[modelindex];
489 if (!model->TraceBox)
490 Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent));
492 if (ent->fields.server->angles[0] || ent->fields.server->angles[2] || ent->fields.server->avelocity[0] || ent->fields.server->avelocity[2])
494 VectorAdd(ent->fields.server->origin, model->rotatedmins, mins);
495 VectorAdd(ent->fields.server->origin, model->rotatedmaxs, maxs);
497 else if (ent->fields.server->angles[1] || ent->fields.server->avelocity[1])
499 VectorAdd(ent->fields.server->origin, model->yawmins, mins);
500 VectorAdd(ent->fields.server->origin, model->yawmaxs, maxs);
504 VectorAdd(ent->fields.server->origin, model->normalmins, mins);
505 VectorAdd(ent->fields.server->origin, model->normalmaxs, maxs);
510 // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
511 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
512 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
517 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, mins);
518 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, maxs);
522 // to make items easier to pick up and allow them to be grabbed off
523 // of shelves, the abs sizes are expanded
525 if ((int)ent->fields.server->flags & FL_ITEM)
536 // because movement is clipped an epsilon away from an actual edge,
537 // we must fully check even when bounding boxes don't quite touch
546 VectorCopy(mins, ent->fields.server->absmin);
547 VectorCopy(maxs, ent->fields.server->absmax);
549 World_LinkEdict(&sv.world, ent, mins, maxs);
551 // if touch_triggers, call touch on all entities overlapping this box
552 if (touch_triggers && ent->fields.server->solid != SOLID_NOT)
553 SV_LinkEdict_TouchAreaGrid(ent);
557 ===============================================================================
561 ===============================================================================
566 SV_TestEntityPosition
568 returns true if the entity is in solid currently
571 static int SV_TestEntityPosition (prvm_edict_t *ent)
573 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
574 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
578 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
580 // q1bsp/hlbsp use hulls and if the entity does not exactly match
581 // a hull size it is incorrectly tested, so this code tries to
582 // 'fix' it slightly...
585 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
586 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
587 VectorSubtract(m2, m1, s);
588 #define EPSILON (1.0f / 32.0f)
589 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
590 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
591 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
592 for (i = 0;i < 8;i++)
594 v[0] = (i & 1) ? m2[0] : m1[0];
595 v[1] = (i & 2) ? m2[1] : m1[1];
596 v[2] = (i & 4) ? m2[2] : m1[2];
597 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
610 void SV_CheckAllEnts (void)
615 // see if any solid entities are inside the final position
616 check = PRVM_NEXT_EDICT(prog->edicts);
617 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
619 if (check->priv.server->free)
621 if (check->fields.server->movetype == MOVETYPE_PUSH
622 || check->fields.server->movetype == MOVETYPE_NONE
623 || check->fields.server->movetype == MOVETYPE_FOLLOW
624 || check->fields.server->movetype == MOVETYPE_NOCLIP)
627 if (SV_TestEntityPosition (check))
628 Con_Print("entity in invalid position\n");
632 // DRESK - Support for Entity Contents Transition Event
635 SV_CheckContentsTransition
637 returns true if entity had a valid contentstransition function call
640 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
642 int bValidFunctionCall;
643 prvm_eval_t *contentstransition;
645 // Default Valid Function Call to False
646 bValidFunctionCall = false;
648 if(ent->fields.server->watertype != nContents)
649 { // Changed Contents
650 // Acquire Contents Transition Function from QC
651 contentstransition = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.contentstransition);
653 if(contentstransition->function)
654 { // Valid Function; Execute
655 // Assign Valid Function
656 bValidFunctionCall = true;
657 // Prepare Parameters (Original Contents, New Contents)
659 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
661 PRVM_G_FLOAT(OFS_PARM1) = nContents;
662 // Execute VM Function
663 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
667 // Return if Function Call was Valid
668 return bValidFunctionCall;
677 void SV_CheckVelocity (prvm_edict_t *ent)
685 for (i=0 ; i<3 ; i++)
687 if (IS_NAN(ent->fields.server->velocity[i]))
689 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
690 ent->fields.server->velocity[i] = 0;
692 if (IS_NAN(ent->fields.server->origin[i]))
694 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
695 ent->fields.server->origin[i] = 0;
699 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
700 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
701 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
703 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
704 ent->fields.server->velocity[0] *= wishspeed;
705 ent->fields.server->velocity[1] *= wishspeed;
706 ent->fields.server->velocity[2] *= wishspeed;
714 Runs thinking code if time. There is some play in the exact time the think
715 function will be called, because it is called before any movement is done
716 in a frame. Not used for pushmove objects, because they must be exact.
717 Returns false if the entity removed itself.
720 qboolean SV_RunThink (prvm_edict_t *ent)
724 thinktime = ent->fields.server->nextthink;
725 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
728 // don't let things stay in the past.
729 // it is possible to start that way by a trigger with a local time.
730 if (thinktime < sv.time)
733 ent->fields.server->nextthink = 0;
734 prog->globals.server->time = thinktime;
735 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
736 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
737 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
738 return !ent->priv.server->free;
745 Two entities have touched, so run their touch functions
748 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
750 int old_self, old_other;
751 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
754 old_self = prog->globals.server->self;
755 old_other = prog->globals.server->other;
757 prog->globals.server->time = sv.time;
758 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
760 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
761 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
762 prog->globals.server->trace_allsolid = trace->allsolid;
763 prog->globals.server->trace_startsolid = trace->startsolid;
764 prog->globals.server->trace_fraction = trace->fraction;
765 prog->globals.server->trace_inwater = trace->inwater;
766 prog->globals.server->trace_inopen = trace->inopen;
767 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
768 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
769 prog->globals.server->trace_plane_dist = trace->plane.dist;
771 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
773 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
774 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
775 val->_float = trace->startsupercontents;
776 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
777 val->_float = trace->hitsupercontents;
778 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
779 val->_float = trace->hitq3surfaceflags;
780 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
782 if (trace->hittexture)
783 val->string = PRVM_SetTempString(trace->hittexture->name);
787 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
790 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
792 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
793 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
794 prog->globals.server->trace_allsolid = false;
795 prog->globals.server->trace_startsolid = false;
796 prog->globals.server->trace_fraction = 1;
797 prog->globals.server->trace_inwater = false;
798 prog->globals.server->trace_inopen = true;
799 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
800 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
801 prog->globals.server->trace_plane_dist = 0;
802 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
803 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
805 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
807 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
809 if ((val = PRVM_GETGLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
811 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
814 prog->globals.server->self = old_self;
815 prog->globals.server->other = old_other;
823 Slide off of the impacting object
824 returns the blocked flags (1 = floor, 2 = step / wall)
827 #define STOP_EPSILON 0.1
828 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
833 backoff = -DotProduct (in, normal) * overbounce;
834 VectorMA(in, backoff, normal, out);
836 for (i = 0;i < 3;i++)
837 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
846 The basic solid body movement clip that slides along multiple planes
847 Returns the clipflags if the velocity was modified (hit something solid)
851 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
854 // LordHavoc: increased from 5 to 32
855 #define MAX_CLIP_PLANES 32
856 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
858 int blocked, bumpcount;
859 int i, j, impact, numplanes;
861 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
866 VectorCopy(ent->fields.server->velocity, original_velocity);
867 VectorCopy(ent->fields.server->velocity, primal_velocity);
870 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
872 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
875 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
876 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
878 //if (trace.fraction < 0.002)
883 VectorCopy(ent->fields.server->origin, start);
884 start[2] += 3;//0.03125;
885 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
886 end[2] += 3;//0.03125;
887 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
888 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)))
890 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
896 for (i = 0;i < numplanes;i++)
898 VectorCopy(ent->fields.server->origin, start);
899 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
900 VectorMA(start, 3, planes[i], start);
901 VectorMA(end, 3, planes[i], end);
902 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
903 if (trace.fraction < testtrace.fraction)
906 VectorCopy(start, ent->fields.server->origin);
911 // VectorAdd(ent->fields.server->origin, planes[j], start);
917 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);
918 if (trace.fraction < 1)
919 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
924 if (trace.bmodelstartsolid)
926 // LordHavoc: note: this code is what makes entities stick in place
927 // if embedded in world only (you can walk through other objects if
929 // entity is trapped in another solid
930 VectorClear(ent->fields.server->velocity);
935 // break if it moved the entire distance
936 if (trace.fraction == 1)
938 VectorCopy(trace.endpos, ent->fields.server->origin);
944 Con_Printf ("SV_FlyMove: !trace.ent");
945 trace.ent = prog->edicts;
948 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
952 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
956 if (trace.plane.normal[2])
958 if (trace.plane.normal[2] > 0.7)
962 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
963 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
970 // save the trace for player extrafriction
972 VectorCopy(trace.plane.normal, stepnormal);
975 if (trace.fraction >= 0.001)
977 // actually covered some distance
978 VectorCopy(trace.endpos, ent->fields.server->origin);
979 VectorCopy(ent->fields.server->velocity, original_velocity);
983 // run the impact function
986 SV_Impact(ent, &trace);
988 // break if removed by the impact function
989 if (ent->priv.server->free)
993 time_left *= 1 - trace.fraction;
995 // clipped to another plane
996 if (numplanes >= MAX_CLIP_PLANES)
998 // this shouldn't really happen
999 VectorClear(ent->fields.server->velocity);
1005 for (i = 0;i < numplanes;i++)
1006 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
1010 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
1015 VectorCopy(trace.plane.normal, planes[numplanes]);
1018 if (sv_newflymove.integer)
1019 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
1022 // modify original_velocity so it parallels all of the clip planes
1023 for (i = 0;i < numplanes;i++)
1025 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
1026 for (j = 0;j < numplanes;j++)
1031 if (DotProduct(new_velocity, planes[j]) < 0)
1041 // go along this plane
1042 VectorCopy(new_velocity, ent->fields.server->velocity);
1046 // go along the crease
1049 VectorClear(ent->fields.server->velocity);
1053 CrossProduct(planes[0], planes[1], dir);
1054 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
1055 VectorNormalize(dir);
1056 d = DotProduct(dir, ent->fields.server->velocity);
1057 VectorScale(dir, d, ent->fields.server->velocity);
1061 // if current velocity is against the original velocity,
1062 // stop dead to avoid tiny occilations in sloping corners
1063 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
1065 VectorClear(ent->fields.server->velocity);
1070 //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]);
1073 if ((blocked & 1) == 0 && bumpcount > 1)
1075 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
1076 // flag ONGROUND if there's ground under it
1077 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
1081 // LordHavoc: this came from QW and allows you to get out of water more easily
1082 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
1083 VectorCopy(primal_velocity, ent->fields.server->velocity);
1093 void SV_AddGravity (prvm_edict_t *ent)
1098 val = PRVM_GETEDICTFIELDVALUE(ent, prog->fieldoffsets.gravity);
1099 if (val!=0 && val->_float)
1100 ent_gravity = val->_float;
1103 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
1108 ===============================================================================
1112 ===============================================================================
1119 Does not change the entities velocity at all
1122 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
1128 VectorAdd (ent->fields.server->origin, push, end);
1130 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
1131 type = MOVE_MISSILE;
1132 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
1133 type = MOVE_NOMONSTERS; // only clip against bmodels
1137 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
1138 if (trace.bmodelstartsolid && failonbmodelstartsolid)
1141 VectorCopy (trace.endpos, ent->fields.server->origin);
1142 SV_LinkEdict (ent, true);
1144 if (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)))
1145 SV_Impact (ent, &trace);
1156 void SV_PushMove (prvm_edict_t *pusher, float movetime)
1159 float savesolid, movetime2, pushltime;
1160 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
1162 int numcheckentities;
1163 static prvm_edict_t *checkentities[MAX_EDICTS];
1164 model_t *pushermodel;
1167 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])
1169 pusher->fields.server->ltime += movetime;
1173 switch ((int) pusher->fields.server->solid)
1175 // LordHavoc: valid pusher types
1178 case SOLID_SLIDEBOX:
1179 case SOLID_CORPSE: // LordHavoc: this would be weird...
1181 // LordHavoc: no collisions
1184 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1185 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1186 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1187 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1188 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1189 pusher->fields.server->ltime += movetime;
1190 SV_LinkEdict (pusher, false);
1193 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
1196 index = (int) pusher->fields.server->modelindex;
1197 if (index < 1 || index >= MAX_MODELS)
1199 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
1202 pushermodel = sv.models[index];
1204 movetime2 = movetime;
1205 VectorScale(pusher->fields.server->velocity, movetime2, move1);
1206 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
1207 if (moveangle[0] || moveangle[2])
1209 for (i = 0;i < 3;i++)
1213 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
1214 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1218 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1219 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
1223 else if (moveangle[1])
1225 for (i = 0;i < 3;i++)
1229 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
1230 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1234 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1235 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
1241 for (i = 0;i < 3;i++)
1245 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
1246 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
1250 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
1251 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
1256 VectorNegate (moveangle, a);
1257 AngleVectorsFLU (a, forward, left, up);
1259 VectorCopy (pusher->fields.server->origin, pushorig);
1260 VectorCopy (pusher->fields.server->angles, pushang);
1261 pushltime = pusher->fields.server->ltime;
1263 // move the pusher to its final position
1265 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
1266 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
1267 pusher->fields.server->ltime += movetime;
1268 SV_LinkEdict (pusher, false);
1270 savesolid = pusher->fields.server->solid;
1272 // see if any solid entities are inside the final position
1275 numcheckentities = World_EntitiesInBox(&sv.world, mins, maxs, MAX_EDICTS, checkentities);
1276 for (e = 0;e < numcheckentities;e++)
1278 prvm_edict_t *check = checkentities[e];
1279 if (check->fields.server->movetype == MOVETYPE_NONE
1280 || check->fields.server->movetype == MOVETYPE_PUSH
1281 || check->fields.server->movetype == MOVETYPE_FOLLOW
1282 || check->fields.server->movetype == MOVETYPE_NOCLIP
1283 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
1286 // if the entity is standing on the pusher, it will definitely be moved
1287 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
1289 // remove the onground flag for non-players
1290 if (check->fields.server->movetype != MOVETYPE_WALK)
1291 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
1295 // if the entity is not inside the pusher's final position, leave it alone
1296 if (!SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1301 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
1304 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
1305 org2[0] = DotProduct (org, forward);
1306 org2[1] = DotProduct (org, left);
1307 org2[2] = DotProduct (org, up);
1308 VectorSubtract (org2, org, move);
1309 VectorAdd (move, move1, move);
1312 VectorCopy (move1, move);
1314 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
1315 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
1316 sv.moved_edicts[num_moved++] = check;
1318 // try moving the contacted entity
1319 pusher->fields.server->solid = SOLID_NOT;
1320 trace = SV_PushEntity (check, move, true);
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 // if it is still inside the pusher, block
1327 if (SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1329 // try moving the contacted entity a tiny bit further to account for precision errors
1331 pusher->fields.server->solid = SOLID_NOT;
1332 VectorScale(move, 1.1, move2);
1333 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1334 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1335 SV_PushEntity (check, move2, true);
1336 pusher->fields.server->solid = savesolid;
1337 if (SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1339 // try moving the contacted entity a tiny bit less to account for precision errors
1340 pusher->fields.server->solid = SOLID_NOT;
1341 VectorScale(move, 0.9, move2);
1342 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
1343 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
1344 SV_PushEntity (check, move2, true);
1345 pusher->fields.server->solid = savesolid;
1346 if (SV_Move_ClipToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
1348 // still inside pusher, so it's really blocked
1351 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
1353 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
1356 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
1357 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
1361 VectorCopy (pushorig, pusher->fields.server->origin);
1362 VectorCopy (pushang, pusher->fields.server->angles);
1363 pusher->fields.server->ltime = pushltime;
1364 SV_LinkEdict (pusher, false);
1366 // move back any entities we already moved
1367 for (i = 0;i < num_moved;i++)
1369 prvm_edict_t *ed = sv.moved_edicts[i];
1370 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
1371 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
1372 SV_LinkEdict (ed, false);
1375 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
1376 if (pusher->fields.server->blocked)
1378 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
1379 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
1380 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
1387 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
1388 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
1389 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
1398 void SV_Physics_Pusher (prvm_edict_t *ent)
1400 float thinktime, oldltime, movetime;
1402 oldltime = ent->fields.server->ltime;
1404 thinktime = ent->fields.server->nextthink;
1405 if (thinktime < ent->fields.server->ltime + sv.frametime)
1407 movetime = thinktime - ent->fields.server->ltime;
1412 movetime = sv.frametime;
1415 // advances ent->fields.server->ltime if not blocked
1416 SV_PushMove (ent, movetime);
1418 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
1420 ent->fields.server->nextthink = 0;
1421 prog->globals.server->time = sv.time;
1422 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1423 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1424 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
1430 ===============================================================================
1434 ===============================================================================
1441 This is a big hack to try and fix the rare case of getting stuck in the world
1445 void SV_CheckStuck (prvm_edict_t *ent)
1450 if (!SV_TestEntityPosition(ent))
1452 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
1456 VectorCopy (ent->fields.server->origin, org);
1458 for (z=-1 ; z< 18 ; z++)
1459 for (i=-1 ; i <= 1 ; i++)
1460 for (j=-1 ; j <= 1 ; j++)
1462 ent->fields.server->origin[0] = org[0] + i;
1463 ent->fields.server->origin[1] = org[1] + j;
1464 ent->fields.server->origin[2] = org[2] + z;
1465 if (!SV_TestEntityPosition(ent))
1467 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), (float)i, (float)j, (float)z);
1468 SV_LinkEdict (ent, true);
1473 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
1474 if (!SV_TestEntityPosition(ent))
1476 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1477 SV_LinkEdict (ent, true);
1481 VectorCopy (org, ent->fields.server->origin);
1482 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1485 static void SV_UnstickEntity (prvm_edict_t *ent)
1490 // if not stuck in a bmodel, just return
1491 if (!SV_TestEntityPosition(ent))
1494 VectorCopy (ent->fields.server->origin, org);
1496 for (z=-1 ; z< 18 ; z += 6)
1497 for (i=-1 ; i <= 1 ; i++)
1498 for (j=-1 ; j <= 1 ; j++)
1500 ent->fields.server->origin[0] = org[0] + i;
1501 ent->fields.server->origin[1] = org[1] + j;
1502 ent->fields.server->origin[2] = org[2] + z;
1503 if (!SV_TestEntityPosition(ent))
1505 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), (float)i, (float)j, (float)z);
1506 SV_LinkEdict (ent, true);
1511 VectorCopy (org, ent->fields.server->origin);
1512 if (developer.integer >= 100)
1513 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1522 qboolean SV_CheckWater (prvm_edict_t *ent)
1525 int nNativeContents;
1528 point[0] = ent->fields.server->origin[0];
1529 point[1] = ent->fields.server->origin[1];
1530 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1532 // DRESK - Support for Entity Contents Transition Event
1533 // NOTE: Some logic needed to be slightly re-ordered
1534 // to not affect performance and allow for the feature.
1536 // Acquire Super Contents Prior to Resets
1537 cont = SV_PointSuperContents(point);
1538 // Acquire Native Contents Here
1539 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1541 // DRESK - Support for Entity Contents Transition Event
1542 if(ent->fields.server->watertype)
1543 // Entity did NOT Spawn; Check
1544 SV_CheckContentsTransition(ent, nNativeContents);
1547 ent->fields.server->waterlevel = 0;
1548 ent->fields.server->watertype = CONTENTS_EMPTY;
1549 cont = SV_PointSuperContents(point);
1550 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1552 ent->fields.server->watertype = nNativeContents;
1553 ent->fields.server->waterlevel = 1;
1554 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1555 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1557 ent->fields.server->waterlevel = 2;
1558 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1559 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1560 ent->fields.server->waterlevel = 3;
1564 return ent->fields.server->waterlevel > 1;
1573 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1576 vec3_t forward, into, side;
1578 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1579 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1581 // cut the tangential velocity
1582 i = DotProduct (stepnormal, ent->fields.server->velocity);
1583 VectorScale (stepnormal, i, into);
1584 VectorSubtract (ent->fields.server->velocity, into, side);
1585 ent->fields.server->velocity[0] = side[0] * (1 + d);
1586 ent->fields.server->velocity[1] = side[1] * (1 + d);
1592 =====================
1595 Player has come to a dead stop, possibly due to the problem with limited
1596 float precision at some angle joins in the BSP hull.
1598 Try fixing by pushing one pixel in each direction.
1600 This is a hack, but in the interest of good gameplay...
1601 ======================
1603 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1608 VectorCopy (ent->fields.server->origin, oldorg);
1611 for (i=0 ; i<8 ; i++)
1613 // try pushing a little in an axial direction
1616 case 0: dir[0] = 2; dir[1] = 0; break;
1617 case 1: dir[0] = 0; dir[1] = 2; break;
1618 case 2: dir[0] = -2; dir[1] = 0; break;
1619 case 3: dir[0] = 0; dir[1] = -2; break;
1620 case 4: dir[0] = 2; dir[1] = 2; break;
1621 case 5: dir[0] = -2; dir[1] = 2; break;
1622 case 6: dir[0] = 2; dir[1] = -2; break;
1623 case 7: dir[0] = -2; dir[1] = -2; break;
1626 SV_PushEntity (ent, dir, false);
1628 // retry the original move
1629 ent->fields.server->velocity[0] = oldvel[0];
1630 ent->fields.server->velocity[1] = oldvel[1];
1631 ent->fields.server->velocity[2] = 0;
1632 clip = SV_FlyMove (ent, 0.1, NULL);
1634 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1635 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1637 Con_DPrint("TryUnstick - success.\n");
1641 // go back to the original pos and try again
1642 VectorCopy (oldorg, ent->fields.server->origin);
1646 VectorClear (ent->fields.server->velocity);
1647 Con_DPrint("TryUnstick - failure.\n");
1653 =====================
1656 Only used by players
1657 ======================
1659 void SV_WalkMove (prvm_edict_t *ent)
1661 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1662 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1665 // if frametime is 0 (due to client sending the same timestamp twice),
1667 if (sv.frametime <= 0)
1670 SV_CheckVelocity(ent);
1672 // do a regular slide move unless it looks like you ran into a step
1673 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1674 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1676 VectorCopy (ent->fields.server->origin, start_origin);
1677 VectorCopy (ent->fields.server->velocity, start_velocity);
1679 clip = SV_FlyMove (ent, sv.frametime, NULL);
1681 SV_CheckVelocity(ent);
1683 VectorCopy(ent->fields.server->origin, originalmove_origin);
1684 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1685 originalmove_clip = clip;
1686 originalmove_flags = (int)ent->fields.server->flags;
1687 originalmove_groundentity = ent->fields.server->groundentity;
1689 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1692 if (sv_nostep.integer)
1695 // if move didn't block on a step, return
1698 // if move was not trying to move into the step, return
1699 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1702 if (ent->fields.server->movetype != MOVETYPE_FLY)
1704 // return if gibbed by a trigger
1705 if (ent->fields.server->movetype != MOVETYPE_WALK)
1708 // only step up while jumping if that is enabled
1709 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1710 if (!oldonground && ent->fields.server->waterlevel == 0)
1714 // try moving up and forward to go up a step
1715 // back to start pos
1716 VectorCopy (start_origin, ent->fields.server->origin);
1717 VectorCopy (start_velocity, ent->fields.server->velocity);
1720 VectorClear (upmove);
1721 upmove[2] = sv_stepheight.value;
1722 // FIXME: don't link?
1723 SV_PushEntity(ent, upmove, false);
1726 ent->fields.server->velocity[2] = 0;
1727 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1728 ent->fields.server->velocity[2] += start_velocity[2];
1730 SV_CheckVelocity(ent);
1732 // check for stuckness, possibly due to the limited precision of floats
1733 // in the clipping hulls
1735 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1736 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1738 //Con_Printf("wall\n");
1739 // stepping up didn't make any progress, revert to original move
1740 VectorCopy(originalmove_origin, ent->fields.server->origin);
1741 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1742 //clip = originalmove_clip;
1743 ent->fields.server->flags = originalmove_flags;
1744 ent->fields.server->groundentity = originalmove_groundentity;
1745 // now try to unstick if needed
1746 //clip = SV_TryUnstick (ent, oldvel);
1750 //Con_Printf("step - ");
1752 // extra friction based on view angle
1753 if (clip & 2 && sv_wallfriction.integer)
1754 SV_WallFriction (ent, stepnormal);
1756 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1757 else if (!(sv_gameplayfix_stepdown.integer && ent->fields.server->waterlevel < 2 && start_velocity[2] < (1.0 / 32.0) && oldonground && !((int)ent->fields.server->flags & FL_ONGROUND)))
1761 VectorClear (downmove);
1762 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1763 // FIXME: don't link?
1764 downtrace = SV_PushEntity (ent, downmove, false);
1766 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1768 // this has been disabled so that you can't jump when you are stepping
1769 // up while already jumping (also known as the Quake2 stair jump bug)
1771 // LordHavoc: disabled this check so you can walk on monsters/players
1772 //if (ent->fields.server->solid == SOLID_BSP)
1774 //Con_Printf("onground\n");
1775 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1776 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1782 //Con_Printf("slope\n");
1783 // if the push down didn't end up on good ground, use the move without
1784 // the step up. This happens near wall / slope combinations, and can
1785 // cause the player to hop up higher on a slope too steep to climb
1786 VectorCopy(originalmove_origin, ent->fields.server->origin);
1787 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1788 //clip = originalmove_clip;
1789 ent->fields.server->flags = originalmove_flags;
1790 ent->fields.server->groundentity = originalmove_groundentity;
1793 SV_CheckVelocity(ent);
1796 //============================================================================
1802 Entities that are "stuck" to another entity
1805 void SV_Physics_Follow (prvm_edict_t *ent)
1807 vec3_t vf, vr, vu, angles, v;
1811 if (!SV_RunThink (ent))
1814 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1815 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1816 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])
1818 // quick case for no rotation
1819 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1823 angles[0] = -ent->fields.server->punchangle[0];
1824 angles[1] = ent->fields.server->punchangle[1];
1825 angles[2] = ent->fields.server->punchangle[2];
1826 AngleVectors (angles, vf, vr, vu);
1827 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];
1828 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];
1829 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];
1830 angles[0] = -e->fields.server->angles[0];
1831 angles[1] = e->fields.server->angles[1];
1832 angles[2] = e->fields.server->angles[2];
1833 AngleVectors (angles, vf, vr, vu);
1834 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1835 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1836 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1838 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1839 SV_LinkEdict (ent, true);
1843 ==============================================================================
1847 ==============================================================================
1852 SV_CheckWaterTransition
1856 void SV_CheckWaterTransition (prvm_edict_t *ent)
1859 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1860 if (!ent->fields.server->watertype)
1862 // just spawned here
1863 ent->fields.server->watertype = cont;
1864 ent->fields.server->waterlevel = 1;
1868 // DRESK - Support for Entity Contents Transition Event
1869 // NOTE: Call here BEFORE updating the watertype below,
1870 // and suppress watersplash sound if a valid function
1871 // call was made to allow for custom "splash" sounds.
1872 if( !SV_CheckContentsTransition(ent, cont) )
1873 { // Contents Transition Function Invalid; Potentially Play Water Sound
1874 // check if the entity crossed into or out of water
1875 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1876 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1879 if (cont <= CONTENTS_WATER)
1881 ent->fields.server->watertype = cont;
1882 ent->fields.server->waterlevel = 1;
1886 ent->fields.server->watertype = CONTENTS_EMPTY;
1887 ent->fields.server->waterlevel = 0;
1895 Toss, bounce, and fly movement. When onground, do nothing.
1898 void SV_Physics_Toss (prvm_edict_t *ent)
1903 // if onground, return without moving
1904 if ((int)ent->fields.server->flags & FL_ONGROUND)
1906 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1908 // don't stick to ground if onground and moving upward
1909 ent->fields.server->flags -= FL_ONGROUND;
1911 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1913 // we can trust FL_ONGROUND if groundentity is world because it never moves
1916 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1918 // if ent was supported by a brush model on previous frame,
1919 // and groundentity is now freed, set groundentity to 0 (world)
1920 // which leaves it suspended in the air
1921 ent->fields.server->groundentity = 0;
1925 ent->priv.server->suspendedinairflag = false;
1927 SV_CheckVelocity (ent);
1930 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1931 SV_AddGravity (ent);
1934 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1937 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1938 trace = SV_PushEntity (ent, move, true);
1939 if (ent->priv.server->free)
1941 if (trace.bmodelstartsolid)
1943 // try to unstick the entity
1944 SV_UnstickEntity(ent);
1945 trace = SV_PushEntity (ent, move, false);
1946 if (ent->priv.server->free)
1950 if (trace.fraction < 1)
1952 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1954 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1955 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1957 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1960 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1961 // LordHavoc: fixed grenades not bouncing when fired down a slope
1962 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1964 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1965 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1967 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1968 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1969 VectorClear (ent->fields.server->velocity);
1970 VectorClear (ent->fields.server->avelocity);
1973 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1977 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1979 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1980 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1981 VectorClear (ent->fields.server->velocity);
1982 VectorClear (ent->fields.server->avelocity);
1985 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1990 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1991 if (trace.plane.normal[2] > 0.7)
1993 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1994 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1995 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1996 ent->priv.server->suspendedinairflag = true;
1997 VectorClear (ent->fields.server->velocity);
1998 VectorClear (ent->fields.server->avelocity);
2001 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
2005 // check for in water
2006 SV_CheckWaterTransition (ent);
2010 ===============================================================================
2014 ===============================================================================
2021 Monsters freefall when they don't have a ground entity, otherwise
2022 all movement is done with discrete steps.
2024 This is also used for objects that have become still on the ground, but
2025 will fall if the floor is pulled out from under them.
2028 void SV_Physics_Step (prvm_edict_t *ent)
2030 int flags = (int)ent->fields.server->flags;
2031 // don't fall at all if fly/swim
2032 if (!(flags & (FL_FLY | FL_SWIM)))
2034 if (flags & FL_ONGROUND)
2036 // freefall if onground and moving upward
2037 // freefall if not standing on a world surface (it may be a lift or trap door)
2038 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
2040 ent->fields.server->flags -= FL_ONGROUND;
2042 SV_CheckVelocity(ent);
2043 SV_FlyMove(ent, sv.frametime, NULL);
2044 SV_LinkEdict(ent, true);
2049 // freefall if not onground
2050 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
2053 SV_CheckVelocity(ent);
2054 SV_FlyMove(ent, sv.frametime, NULL);
2055 SV_LinkEdict(ent, true);
2058 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
2059 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
2066 SV_CheckWaterTransition(ent);
2069 //============================================================================
2071 static void SV_Physics_Entity (prvm_edict_t *ent)
2073 // don't run a move on newly spawned projectiles as it messes up movement
2074 // interpolation and rocket trails
2075 qboolean runmove = ent->priv.server->move;
2076 ent->priv.server->move = true;
2077 switch ((int) ent->fields.server->movetype)
2080 case MOVETYPE_FAKEPUSH:
2081 SV_Physics_Pusher (ent);
2084 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2085 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2088 case MOVETYPE_FOLLOW:
2089 SV_Physics_Follow (ent);
2091 case MOVETYPE_NOCLIP:
2092 if (SV_RunThink(ent))
2095 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2096 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2098 SV_LinkEdict(ent, false);
2101 SV_Physics_Step (ent);
2104 if (SV_RunThink (ent))
2106 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2107 SV_AddGravity (ent);
2108 SV_CheckStuck (ent);
2110 SV_LinkEdict (ent, true);
2114 case MOVETYPE_BOUNCE:
2115 case MOVETYPE_BOUNCEMISSILE:
2116 case MOVETYPE_FLYMISSILE:
2119 if (SV_RunThink (ent) && runmove)
2120 SV_Physics_Toss (ent);
2123 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
2128 void SV_Physics_ClientEntity (prvm_edict_t *ent)
2130 SV_ApplyClientMove();
2131 // make sure the velocity is sane (not a NaN)
2132 SV_CheckVelocity(ent);
2133 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
2134 if (prog->funcoffsets.SV_PlayerPhysics && sv_playerphysicsqc.integer)
2136 prog->globals.server->time = sv.time;
2137 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2138 PRVM_ExecuteProgram (prog->funcoffsets.SV_PlayerPhysics, "QC function SV_PlayerPhysics is missing");
2142 // make sure the velocity is sane (not a NaN)
2143 SV_CheckVelocity(ent);
2144 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
2145 // player_run/player_stand1 does not horribly malfunction if the
2146 // velocity becomes a number that is both == 0 and != 0
2147 // (sounds to me like NaN but to be absolutely safe...)
2148 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
2149 VectorClear(ent->fields.server->velocity);
2150 // call standard client pre-think
2151 prog->globals.server->time = sv.time;
2152 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2153 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
2154 SV_CheckVelocity (ent);
2156 switch ((int) ent->fields.server->movetype)
2159 case MOVETYPE_FAKEPUSH:
2160 SV_Physics_Pusher (ent);
2163 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
2164 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
2167 case MOVETYPE_FOLLOW:
2168 SV_Physics_Follow (ent);
2170 case MOVETYPE_NOCLIP:
2171 if (SV_RunThink(ent))
2174 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
2175 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
2179 SV_Physics_Step (ent);
2182 if (SV_RunThink (ent))
2184 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
2185 SV_AddGravity (ent);
2186 SV_CheckStuck (ent);
2191 case MOVETYPE_BOUNCE:
2192 case MOVETYPE_BOUNCEMISSILE:
2193 case MOVETYPE_FLYMISSILE:
2195 if (SV_RunThink (ent))
2196 SV_Physics_Toss (ent);
2199 if (SV_RunThink (ent))
2201 SV_CheckWater (ent);
2206 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
2210 SV_CheckVelocity (ent);
2212 // call standard player post-think
2213 SV_LinkEdict (ent, true);
2215 SV_CheckVelocity (ent);
2217 prog->globals.server->time = sv.time;
2218 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
2219 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
2228 void SV_Physics (void)
2233 // let the progs know that a new frame has started
2234 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2235 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2236 prog->globals.server->time = sv.time;
2237 prog->globals.server->frametime = sv.frametime;
2238 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
2241 // treat each object in turn
2244 // if force_retouch, relink all the entities
2245 if (prog->globals.server->force_retouch > 0)
2246 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2247 if (!ent->priv.server->free)
2248 SV_LinkEdict (ent, true); // force retouch even for stationary
2250 // run physics on the client entities
2251 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
2253 if (!ent->priv.server->free)
2255 // don't do physics on disconnected clients, FrikBot relies on this
2256 if (!host_client->spawned)
2257 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
2258 // don't run physics here if running asynchronously
2259 else if (host_client->clmovement_skipphysicsframes > 0)
2260 host_client->clmovement_skipphysicsframes--;
2262 SV_Physics_ClientEntity(ent);
2266 // run physics on all the non-client entities
2267 if (!sv_freezenonclients.integer)
2268 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
2269 if (!ent->priv.server->free)
2270 SV_Physics_Entity(ent);
2272 if (prog->globals.server->force_retouch > 0)
2273 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
2275 // LordHavoc: endframe support
2276 if (prog->funcoffsets.EndFrame)
2278 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
2279 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
2280 prog->globals.server->time = sv.time;
2281 PRVM_ExecuteProgram (prog->funcoffsets.EndFrame, "QC function EndFrame is missing");
2284 // decrement prog->num_edicts if the highest number entities died
2285 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
2287 if (!sv_freezenonclients.integer)
2288 sv.time += sv.frametime;
2292 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
2297 vec3_t original_origin;
2298 vec3_t original_velocity;
2299 vec3_t original_angles;
2300 vec3_t original_avelocity;
2304 VectorCopy(tossent->fields.server->origin , original_origin );
2305 VectorCopy(tossent->fields.server->velocity , original_velocity );
2306 VectorCopy(tossent->fields.server->angles , original_angles );
2307 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
2309 val = PRVM_GETEDICTFIELDVALUE(tossent, prog->fieldoffsets.gravity);
2310 if (val != NULL && val->_float != 0)
2311 gravity = val->_float;
2314 gravity *= sv_gravity.value * 0.05;
2316 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
2318 SV_CheckVelocity (tossent);
2319 tossent->fields.server->velocity[2] -= gravity;
2320 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
2321 VectorScale (tossent->fields.server->velocity, 0.05, move);
2322 VectorAdd (tossent->fields.server->origin, move, end);
2323 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
2324 VectorCopy (trace.endpos, tossent->fields.server->origin);
2326 if (trace.fraction < 1)
2330 VectorCopy(original_origin , tossent->fields.server->origin );
2331 VectorCopy(original_velocity , tossent->fields.server->velocity );
2332 VectorCopy(original_angles , tossent->fields.server->angles );
2333 VectorCopy(original_avelocity, tossent->fields.server->avelocity);