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.
23 // used only for VM_GetTempString
24 #include "prvm_cmds.h"
29 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.
31 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
33 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
34 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
35 corpses are SOLID_NOT and MOVETYPE_TOSS
36 crates are SOLID_BBOX and MOVETYPE_TOSS
37 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
38 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
40 solid_edge items only clip against bsp models.
44 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
45 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
46 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
47 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
48 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
49 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
50 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
51 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
52 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
53 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
54 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
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 #define MOVE_EPSILON 0.01
61 void SV_Physics_Toss (prvm_edict_t *ent);
63 void SV_Phys_Init (void)
65 Cvar_RegisterVariable(&sv_stepheight);
66 Cvar_RegisterVariable(&sv_jumpstep);
67 Cvar_RegisterVariable(&sv_wallfriction);
68 Cvar_RegisterVariable(&sv_newflymove);
69 Cvar_RegisterVariable(&sv_freezenonclients);
71 Cvar_RegisterVariable(&sv_playerphysicsqc);
73 Cvar_RegisterVariable(&sv_sound_watersplash);
74 Cvar_RegisterVariable(&sv_sound_land);
81 returns true if the entity is in solid currently
84 static int SV_TestEntityPosition (prvm_edict_t *ent)
86 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
87 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
98 void SV_CheckAllEnts (void)
103 // see if any solid entities are inside the final position
104 check = PRVM_NEXT_EDICT(prog->edicts);
105 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
107 if (check->priv.server->free)
109 if (check->fields.server->movetype == MOVETYPE_PUSH
110 || check->fields.server->movetype == MOVETYPE_NONE
111 || check->fields.server->movetype == MOVETYPE_FOLLOW
112 || check->fields.server->movetype == MOVETYPE_NOCLIP)
115 if (SV_TestEntityPosition (check))
116 Con_Print("entity in invalid position\n");
125 void SV_CheckVelocity (prvm_edict_t *ent)
133 for (i=0 ; i<3 ; i++)
135 if (IS_NAN(ent->fields.server->velocity[i]))
137 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
138 ent->fields.server->velocity[i] = 0;
140 if (IS_NAN(ent->fields.server->origin[i]))
142 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
143 ent->fields.server->origin[i] = 0;
147 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
148 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
149 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
151 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
152 ent->fields.server->velocity[0] *= wishspeed;
153 ent->fields.server->velocity[1] *= wishspeed;
154 ent->fields.server->velocity[2] *= wishspeed;
162 Runs thinking code if time. There is some play in the exact time the think
163 function will be called, because it is called before any movement is done
164 in a frame. Not used for pushmove objects, because they must be exact.
165 Returns false if the entity removed itself.
168 qboolean SV_RunThink (prvm_edict_t *ent)
172 thinktime = ent->fields.server->nextthink;
173 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
176 // don't let things stay in the past.
177 // it is possible to start that way by a trigger with a local time.
178 if (thinktime < sv.time)
181 ent->fields.server->nextthink = 0;
182 prog->globals.server->time = thinktime;
183 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
184 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
185 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
186 return !ent->priv.server->free;
193 Two entities have touched, so run their touch functions
196 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
198 int old_self, old_other;
199 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
202 old_self = prog->globals.server->self;
203 old_other = prog->globals.server->other;
205 prog->globals.server->time = sv.time;
206 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
208 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
209 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
210 prog->globals.server->trace_allsolid = trace->allsolid;
211 prog->globals.server->trace_startsolid = trace->startsolid;
212 prog->globals.server->trace_fraction = trace->fraction;
213 prog->globals.server->trace_inwater = trace->inwater;
214 prog->globals.server->trace_inopen = trace->inopen;
215 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
216 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
217 prog->globals.server->trace_plane_dist = trace->plane.dist;
219 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
221 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
222 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
223 val->_float = trace->startsupercontents;
224 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
225 val->_float = trace->hitsupercontents;
226 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
227 val->_float = trace->hitq3surfaceflags;
228 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
230 if (trace->hittexture)
232 char *s = VM_GetTempString();
233 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
234 val->string = PRVM_SetEngineString(s);
239 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
242 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
244 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
245 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
246 prog->globals.server->trace_allsolid = false;
247 prog->globals.server->trace_startsolid = false;
248 prog->globals.server->trace_fraction = 1;
249 prog->globals.server->trace_inwater = false;
250 prog->globals.server->trace_inopen = true;
251 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
252 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
253 prog->globals.server->trace_plane_dist = 0;
254 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
255 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
257 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
259 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
261 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
263 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
266 prog->globals.server->self = old_self;
267 prog->globals.server->other = old_other;
275 Slide off of the impacting object
276 returns the blocked flags (1 = floor, 2 = step / wall)
279 #define STOP_EPSILON 0.1
280 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
285 backoff = -DotProduct (in, normal) * overbounce;
286 VectorMA(in, backoff, normal, out);
288 for (i = 0;i < 3;i++)
289 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
298 The basic solid body movement clip that slides along multiple planes
299 Returns the clipflags if the velocity was modified (hit something solid)
303 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
306 // LordHavoc: increased from 5 to 32
307 #define MAX_CLIP_PLANES 32
308 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
310 int blocked, bumpcount;
311 int i, j, impact, numplanes;
313 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
316 VectorCopy(ent->fields.server->velocity, original_velocity);
317 VectorCopy(ent->fields.server->velocity, primal_velocity);
320 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
322 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
325 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
326 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
328 //if (trace.fraction < 0.002)
333 VectorCopy(ent->fields.server->origin, start);
334 start[2] += 3;//0.03125;
335 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
336 end[2] += 3;//0.03125;
337 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
338 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)))
340 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
346 for (i = 0;i < numplanes;i++)
348 VectorCopy(ent->fields.server->origin, start);
349 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
350 VectorMA(start, 3, planes[i], start);
351 VectorMA(end, 3, planes[i], end);
352 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
353 if (trace.fraction < testtrace.fraction)
356 VectorCopy(start, ent->fields.server->origin);
361 // VectorAdd(ent->fields.server->origin, planes[j], start);
367 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);
368 if (trace.fraction < 1)
369 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
374 if (trace.startsolid)
376 // LordHavoc: note: this code is what makes entities stick in place if embedded in another object (which can be the world)
377 // entity is trapped in another solid
378 VectorClear(ent->fields.server->velocity);
383 // break if it moved the entire distance
384 if (trace.fraction == 1)
386 VectorCopy(trace.endpos, ent->fields.server->origin);
392 Con_Printf ("SV_FlyMove: !trace.ent");
393 trace.ent = prog->edicts;
396 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
400 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
404 if (trace.plane.normal[2])
406 if (trace.plane.normal[2] > 0.7)
410 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
411 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
418 // save the trace for player extrafriction
420 VectorCopy(trace.plane.normal, stepnormal);
423 if (trace.fraction >= 0.001)
425 // actually covered some distance
426 VectorCopy(trace.endpos, ent->fields.server->origin);
427 VectorCopy(ent->fields.server->velocity, original_velocity);
431 // run the impact function
434 SV_Impact(ent, &trace);
436 // break if removed by the impact function
437 if (ent->priv.server->free)
441 time_left *= 1 - trace.fraction;
443 // clipped to another plane
444 if (numplanes >= MAX_CLIP_PLANES)
446 // this shouldn't really happen
447 VectorClear(ent->fields.server->velocity);
453 for (i = 0;i < numplanes;i++)
454 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
458 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
463 VectorCopy(trace.plane.normal, planes[numplanes]);
466 if (sv_newflymove.integer)
467 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
470 // modify original_velocity so it parallels all of the clip planes
471 for (i = 0;i < numplanes;i++)
473 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
474 for (j = 0;j < numplanes;j++)
479 if (DotProduct(new_velocity, planes[j]) < 0)
489 // go along this plane
490 VectorCopy(new_velocity, ent->fields.server->velocity);
494 // go along the crease
497 VectorClear(ent->fields.server->velocity);
501 CrossProduct(planes[0], planes[1], dir);
502 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
503 VectorNormalize(dir);
504 d = DotProduct(dir, ent->fields.server->velocity);
505 VectorScale(dir, d, ent->fields.server->velocity);
509 // if current velocity is against the original velocity,
510 // stop dead to avoid tiny occilations in sloping corners
511 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
513 VectorClear(ent->fields.server->velocity);
518 //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]);
521 if ((blocked & 1) == 0 && bumpcount > 1)
523 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
524 // flag ONGROUND if there's ground under it
525 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
537 void SV_AddGravity (prvm_edict_t *ent)
542 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
543 if (val!=0 && val->_float)
544 ent_gravity = val->_float;
547 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
552 ===============================================================================
556 ===============================================================================
563 Does not change the entities velocity at all
566 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
572 VectorAdd (ent->fields.server->origin, push, end);
574 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
576 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
577 type = MOVE_NOMONSTERS; // only clip against bmodels
581 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
583 VectorCopy (trace.endpos, ent->fields.server->origin);
584 SV_LinkEdict (ent, true);
586 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
587 SV_Impact (ent, &trace);
598 void SV_PushMove (prvm_edict_t *pusher, float movetime)
601 float savesolid, movetime2, pushltime;
602 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
604 int numcheckentities;
605 static prvm_edict_t *checkentities[MAX_EDICTS];
606 model_t *pushermodel;
609 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])
611 pusher->fields.server->ltime += movetime;
615 switch ((int) pusher->fields.server->solid)
617 // LordHavoc: valid pusher types
621 case SOLID_CORPSE: // LordHavoc: this would be weird...
623 // LordHavoc: no collisions
626 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
627 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
628 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
629 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
630 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
631 pusher->fields.server->ltime += movetime;
632 SV_LinkEdict (pusher, false);
635 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
638 index = (int) pusher->fields.server->modelindex;
639 if (index < 1 || index >= MAX_MODELS)
641 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
644 pushermodel = sv.models[index];
646 movetime2 = movetime;
647 VectorScale(pusher->fields.server->velocity, movetime2, move1);
648 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
649 if (moveangle[0] || moveangle[2])
651 for (i = 0;i < 3;i++)
655 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
656 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
660 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
661 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
665 else if (moveangle[1])
667 for (i = 0;i < 3;i++)
671 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
672 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
676 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
677 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
683 for (i = 0;i < 3;i++)
687 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
688 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
692 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
693 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
698 VectorNegate (moveangle, a);
699 AngleVectorsFLU (a, forward, left, up);
701 VectorCopy (pusher->fields.server->origin, pushorig);
702 VectorCopy (pusher->fields.server->angles, pushang);
703 pushltime = pusher->fields.server->ltime;
705 // move the pusher to its final position
707 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
708 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
709 pusher->fields.server->ltime += movetime;
710 SV_LinkEdict (pusher, false);
712 savesolid = pusher->fields.server->solid;
714 // see if any solid entities are inside the final position
717 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
718 for (e = 0;e < numcheckentities;e++)
720 prvm_edict_t *check = checkentities[e];
721 if (check->fields.server->movetype == MOVETYPE_NONE
722 || check->fields.server->movetype == MOVETYPE_PUSH
723 || check->fields.server->movetype == MOVETYPE_FOLLOW
724 || check->fields.server->movetype == MOVETYPE_NOCLIP
725 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
728 // if the entity is standing on the pusher, it will definitely be moved
729 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
731 // if the entity is not inside the pusher's final position, leave it alone
732 if (!SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
734 // remove the onground flag for non-players
735 if (check->fields.server->movetype != MOVETYPE_WALK)
736 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
740 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
743 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
744 org2[0] = DotProduct (org, forward);
745 org2[1] = DotProduct (org, left);
746 org2[2] = DotProduct (org, up);
747 VectorSubtract (org2, org, move);
748 VectorAdd (move, move1, move);
751 VectorCopy (move1, move);
753 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
754 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
755 sv.moved_edicts[num_moved++] = check;
757 // try moving the contacted entity
758 pusher->fields.server->solid = SOLID_NOT;
759 trace = SV_PushEntity (check, move);
760 // FIXME: turn players specially
761 check->fields.server->angles[1] += trace.fraction * moveangle[1];
762 pusher->fields.server->solid = savesolid; // was SOLID_BSP
764 // if it is still inside the pusher, block
765 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
767 // try moving the contacted entity a tiny bit further to account for precision errors
769 pusher->fields.server->solid = SOLID_NOT;
770 VectorScale(move, 1.1, move2);
771 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
772 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
773 SV_PushEntity (check, move2);
774 pusher->fields.server->solid = savesolid;
775 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
777 // try moving the contacted entity a tiny bit less to account for precision errors
778 pusher->fields.server->solid = SOLID_NOT;
779 VectorScale(move, 0.9, move2);
780 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
781 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
782 SV_PushEntity (check, move2);
783 pusher->fields.server->solid = savesolid;
784 if (SV_ClipMoveToEntity(pusher, check->fields.server->origin, check->fields.server->mins, check->fields.server->maxs, check->fields.server->origin, 0, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY).startsolid)
786 // still inside pusher, so it's really blocked
789 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
791 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
794 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
795 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
799 VectorCopy (pushorig, pusher->fields.server->origin);
800 VectorCopy (pushang, pusher->fields.server->angles);
801 pusher->fields.server->ltime = pushltime;
802 SV_LinkEdict (pusher, false);
804 // move back any entities we already moved
805 for (i = 0;i < num_moved;i++)
807 prvm_edict_t *ed = sv.moved_edicts[i];
808 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
809 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
810 SV_LinkEdict (ed, false);
813 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
814 if (pusher->fields.server->blocked)
816 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
817 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
818 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
825 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
826 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
827 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
836 void SV_Physics_Pusher (prvm_edict_t *ent)
838 float thinktime, oldltime, movetime;
840 oldltime = ent->fields.server->ltime;
842 thinktime = ent->fields.server->nextthink;
843 if (thinktime < ent->fields.server->ltime + sv.frametime)
845 movetime = thinktime - ent->fields.server->ltime;
850 movetime = sv.frametime;
853 // advances ent->fields.server->ltime if not blocked
854 SV_PushMove (ent, movetime);
856 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
858 ent->fields.server->nextthink = 0;
859 prog->globals.server->time = sv.time;
860 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
861 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
862 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
868 ===============================================================================
872 ===============================================================================
879 This is a big hack to try and fix the rare case of getting stuck in the world
883 void SV_CheckStuck (prvm_edict_t *ent)
888 if (!SV_TestEntityPosition(ent))
890 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
894 VectorCopy (ent->fields.server->origin, org);
895 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
896 if (!SV_TestEntityPosition(ent))
898 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
899 SV_LinkEdict (ent, true);
903 for (z=-1 ; z< 18 ; z++)
904 for (i=-1 ; i <= 1 ; i++)
905 for (j=-1 ; j <= 1 ; j++)
907 ent->fields.server->origin[0] = org[0] + i;
908 ent->fields.server->origin[1] = org[1] + j;
909 ent->fields.server->origin[2] = org[2] + z;
910 if (!SV_TestEntityPosition(ent))
912 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);
913 SV_LinkEdict (ent, true);
918 VectorCopy (org, ent->fields.server->origin);
919 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
922 static void SV_UnstickEntity (prvm_edict_t *ent)
927 // if not stuck in a bmodel, just return
928 if (!SV_TestEntityPosition(ent))
931 VectorCopy (ent->fields.server->origin, org);
933 for (z=-1 ; z< 18 ; z += 6)
934 for (i=-1 ; i <= 1 ; i++)
935 for (j=-1 ; j <= 1 ; j++)
937 ent->fields.server->origin[0] = org[0] + i;
938 ent->fields.server->origin[1] = org[1] + j;
939 ent->fields.server->origin[2] = org[2] + z;
940 if (!SV_TestEntityPosition(ent))
942 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);
943 SV_LinkEdict (ent, true);
948 VectorCopy (org, ent->fields.server->origin);
949 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
958 qboolean SV_CheckWater (prvm_edict_t *ent)
963 point[0] = ent->fields.server->origin[0];
964 point[1] = ent->fields.server->origin[1];
965 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
967 ent->fields.server->waterlevel = 0;
968 ent->fields.server->watertype = CONTENTS_EMPTY;
969 cont = SV_PointSuperContents(point);
970 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
972 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
973 ent->fields.server->waterlevel = 1;
974 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
975 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
977 ent->fields.server->waterlevel = 2;
978 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
979 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
980 ent->fields.server->waterlevel = 3;
984 return ent->fields.server->waterlevel > 1;
993 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
996 vec3_t forward, into, side;
998 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
999 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1001 // cut the tangential velocity
1002 i = DotProduct (stepnormal, ent->fields.server->velocity);
1003 VectorScale (stepnormal, i, into);
1004 VectorSubtract (ent->fields.server->velocity, into, side);
1005 ent->fields.server->velocity[0] = side[0] * (1 + d);
1006 ent->fields.server->velocity[1] = side[1] * (1 + d);
1011 =====================
1014 Player has come to a dead stop, possibly due to the problem with limited
1015 float precision at some angle joins in the BSP hull.
1017 Try fixing by pushing one pixel in each direction.
1019 This is a hack, but in the interest of good gameplay...
1020 ======================
1022 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1027 VectorCopy (ent->fields.server->origin, oldorg);
1030 for (i=0 ; i<8 ; i++)
1032 // try pushing a little in an axial direction
1035 case 0: dir[0] = 2; dir[1] = 0; break;
1036 case 1: dir[0] = 0; dir[1] = 2; break;
1037 case 2: dir[0] = -2; dir[1] = 0; break;
1038 case 3: dir[0] = 0; dir[1] = -2; break;
1039 case 4: dir[0] = 2; dir[1] = 2; break;
1040 case 5: dir[0] = -2; dir[1] = 2; break;
1041 case 6: dir[0] = 2; dir[1] = -2; break;
1042 case 7: dir[0] = -2; dir[1] = -2; break;
1045 SV_PushEntity (ent, dir);
1047 // retry the original move
1048 ent->fields.server->velocity[0] = oldvel[0];
1049 ent->fields.server->velocity[1] = oldvel[1];
1050 ent->fields.server->velocity[2] = 0;
1051 clip = SV_FlyMove (ent, 0.1, NULL);
1053 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1054 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1056 Con_DPrint("TryUnstick - success.\n");
1060 // go back to the original pos and try again
1061 VectorCopy (oldorg, ent->fields.server->origin);
1065 VectorClear (ent->fields.server->velocity);
1066 Con_DPrint("TryUnstick - failure.\n");
1071 =====================
1074 Only used by players
1075 ======================
1077 void SV_WalkMove (prvm_edict_t *ent)
1079 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1080 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1083 SV_CheckVelocity(ent);
1085 // do a regular slide move unless it looks like you ran into a step
1086 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1087 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1089 VectorCopy (ent->fields.server->origin, start_origin);
1090 VectorCopy (ent->fields.server->velocity, start_velocity);
1092 clip = SV_FlyMove (ent, sv.frametime, NULL);
1094 SV_CheckVelocity(ent);
1096 VectorCopy(ent->fields.server->origin, originalmove_origin);
1097 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1098 originalmove_clip = clip;
1099 originalmove_flags = (int)ent->fields.server->flags;
1100 originalmove_groundentity = ent->fields.server->groundentity;
1102 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1105 if (sv_nostep.integer)
1108 // if move didn't block on a step, return
1111 // if move was not trying to move into the step, return
1112 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1115 if (ent->fields.server->movetype != MOVETYPE_FLY)
1117 // return if gibbed by a trigger
1118 if (ent->fields.server->movetype != MOVETYPE_WALK)
1121 // only step up while jumping if that is enabled
1122 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1123 if (!oldonground && ent->fields.server->waterlevel == 0)
1127 // try moving up and forward to go up a step
1128 // back to start pos
1129 VectorCopy (start_origin, ent->fields.server->origin);
1130 VectorCopy (start_velocity, ent->fields.server->velocity);
1133 VectorClear (upmove);
1134 upmove[2] = sv_stepheight.value;
1135 // FIXME: don't link?
1136 SV_PushEntity(ent, upmove);
1139 ent->fields.server->velocity[2] = 0;
1140 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1141 ent->fields.server->velocity[2] += start_velocity[2];
1143 SV_CheckVelocity(ent);
1145 // check for stuckness, possibly due to the limited precision of floats
1146 // in the clipping hulls
1148 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1149 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1151 //Con_Printf("wall\n");
1152 // stepping up didn't make any progress, revert to original move
1153 VectorCopy(originalmove_origin, ent->fields.server->origin);
1154 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1155 //clip = originalmove_clip;
1156 ent->fields.server->flags = originalmove_flags;
1157 ent->fields.server->groundentity = originalmove_groundentity;
1158 // now try to unstick if needed
1159 //clip = SV_TryUnstick (ent, oldvel);
1163 //Con_Printf("step - ");
1165 // extra friction based on view angle
1166 if (clip & 2 && sv_wallfriction.integer)
1167 SV_WallFriction (ent, stepnormal);
1169 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1170 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)))
1174 VectorClear (downmove);
1175 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1176 // FIXME: don't link?
1177 downtrace = SV_PushEntity (ent, downmove);
1179 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1181 // this has been disabled so that you can't jump when you are stepping
1182 // up while already jumping (also known as the Quake2 stair jump bug)
1184 // LordHavoc: disabled this check so you can walk on monsters/players
1185 //if (ent->fields.server->solid == SOLID_BSP)
1187 //Con_Printf("onground\n");
1188 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1189 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1195 //Con_Printf("slope\n");
1196 // if the push down didn't end up on good ground, use the move without
1197 // the step up. This happens near wall / slope combinations, and can
1198 // cause the player to hop up higher on a slope too steep to climb
1199 VectorCopy(originalmove_origin, ent->fields.server->origin);
1200 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1201 //clip = originalmove_clip;
1202 ent->fields.server->flags = originalmove_flags;
1203 ent->fields.server->groundentity = originalmove_groundentity;
1206 SV_CheckVelocity(ent);
1209 //============================================================================
1215 Entities that are "stuck" to another entity
1218 void SV_Physics_Follow (prvm_edict_t *ent)
1220 vec3_t vf, vr, vu, angles, v;
1224 if (!SV_RunThink (ent))
1227 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1228 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1229 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])
1231 // quick case for no rotation
1232 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1236 angles[0] = -ent->fields.server->punchangle[0];
1237 angles[1] = ent->fields.server->punchangle[1];
1238 angles[2] = ent->fields.server->punchangle[2];
1239 AngleVectors (angles, vf, vr, vu);
1240 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];
1241 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];
1242 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];
1243 angles[0] = -e->fields.server->angles[0];
1244 angles[1] = e->fields.server->angles[1];
1245 angles[2] = e->fields.server->angles[2];
1246 AngleVectors (angles, vf, vr, vu);
1247 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1248 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1249 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1251 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1252 SV_LinkEdict (ent, true);
1256 ==============================================================================
1260 ==============================================================================
1265 SV_CheckWaterTransition
1269 void SV_CheckWaterTransition (prvm_edict_t *ent)
1272 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1273 if (!ent->fields.server->watertype)
1275 // just spawned here
1276 ent->fields.server->watertype = cont;
1277 ent->fields.server->waterlevel = 1;
1281 // check if the entity crossed into or out of water
1282 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1283 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1285 if (cont <= CONTENTS_WATER)
1287 ent->fields.server->watertype = cont;
1288 ent->fields.server->waterlevel = 1;
1292 ent->fields.server->watertype = CONTENTS_EMPTY;
1293 ent->fields.server->waterlevel = 0;
1301 Toss, bounce, and fly movement. When onground, do nothing.
1304 void SV_Physics_Toss (prvm_edict_t *ent)
1309 // if onground, return without moving
1310 if ((int)ent->fields.server->flags & FL_ONGROUND)
1312 // don't stick to ground if onground and moving upward
1313 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1314 ent->fields.server->flags -= FL_ONGROUND;
1317 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1318 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1320 // if ent was supported by a brush model on previous frame,
1321 // and groundentity is now freed, set groundentity to 0 (floating)
1322 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1324 // leave it suspended in the air
1325 ent->fields.server->groundentity = 0;
1330 ent->priv.server->suspendedinairflag = false;
1332 SV_CheckVelocity (ent);
1335 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1336 SV_AddGravity (ent);
1339 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1342 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1343 trace = SV_PushEntity (ent, move);
1344 if (ent->priv.server->free)
1346 if (trace.bmodelstartsolid)
1348 // try to unstick the entity
1349 SV_UnstickEntity(ent);
1350 trace = SV_PushEntity (ent, move);
1351 if (ent->priv.server->free)
1355 if (trace.fraction < 1)
1357 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1359 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1360 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1362 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1365 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1366 // LordHavoc: fixed grenades not bouncing when fired down a slope
1367 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1369 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1370 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1372 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1373 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1374 VectorClear (ent->fields.server->velocity);
1375 VectorClear (ent->fields.server->avelocity);
1378 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1382 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1384 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1385 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1386 VectorClear (ent->fields.server->velocity);
1387 VectorClear (ent->fields.server->avelocity);
1390 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1395 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1396 if (trace.plane.normal[2] > 0.7)
1398 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1399 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1400 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1401 ent->priv.server->suspendedinairflag = true;
1402 VectorClear (ent->fields.server->velocity);
1403 VectorClear (ent->fields.server->avelocity);
1406 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1410 // check for in water
1411 SV_CheckWaterTransition (ent);
1415 ===============================================================================
1419 ===============================================================================
1426 Monsters freefall when they don't have a ground entity, otherwise
1427 all movement is done with discrete steps.
1429 This is also used for objects that have become still on the ground, but
1430 will fall if the floor is pulled out from under them.
1433 void SV_Physics_Step (prvm_edict_t *ent)
1435 int flags = (int)ent->fields.server->flags;
1436 // don't fall at all if fly/swim
1437 if (!(flags & (FL_FLY | FL_SWIM)))
1439 if (flags & FL_ONGROUND)
1441 // freefall if onground and moving upward
1442 // freefall if not standing on a world surface (it may be a lift)
1443 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1444 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1446 ent->fields.server->flags -= FL_ONGROUND;
1448 SV_CheckVelocity(ent);
1449 SV_FlyMove(ent, sv.frametime, NULL);
1450 SV_LinkEdict(ent, true);
1455 // freefall if not onground
1456 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1459 SV_CheckVelocity(ent);
1460 SV_FlyMove(ent, sv.frametime, NULL);
1461 SV_LinkEdict(ent, true);
1464 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1465 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1472 SV_CheckWaterTransition(ent);
1475 //============================================================================
1477 static void SV_Physics_Entity (prvm_edict_t *ent)
1479 // don't run a move on newly spawned projectiles as it messes up movement
1480 // interpolation and rocket trails
1481 qboolean runmove = ent->priv.server->move;
1482 ent->priv.server->move = true;
1483 switch ((int) ent->fields.server->movetype)
1486 case MOVETYPE_FAKEPUSH:
1487 SV_Physics_Pusher (ent);
1490 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1491 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1494 case MOVETYPE_FOLLOW:
1495 SV_Physics_Follow (ent);
1497 case MOVETYPE_NOCLIP:
1498 if (SV_RunThink(ent))
1501 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1502 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1504 SV_LinkEdict(ent, false);
1507 SV_Physics_Step (ent);
1510 if (SV_RunThink (ent))
1512 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1513 SV_AddGravity (ent);
1514 SV_CheckStuck (ent);
1516 SV_LinkEdict (ent, true);
1520 case MOVETYPE_BOUNCE:
1521 case MOVETYPE_BOUNCEMISSILE:
1522 case MOVETYPE_FLYMISSILE:
1525 if (SV_RunThink (ent) && runmove)
1526 SV_Physics_Toss (ent);
1529 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1534 void SV_ApplyClientMove (void);
1535 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1537 SV_ApplyClientMove();
1538 // make sure the velocity is sane (not a NaN)
1539 SV_CheckVelocity(ent);
1540 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1541 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1543 prog->globals.server->time = sv.time;
1544 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1545 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1549 // make sure the velocity is sane (not a NaN)
1550 SV_CheckVelocity(ent);
1551 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1552 // player_run/player_stand1 does not horribly malfunction if the
1553 // velocity becomes a number that is both == 0 and != 0
1554 // (sounds to me like NaN but to be absolutely safe...)
1555 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1556 VectorClear(ent->fields.server->velocity);
1557 // call standard client pre-think
1558 prog->globals.server->time = sv.time;
1559 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1560 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1561 SV_CheckVelocity (ent);
1563 switch ((int) ent->fields.server->movetype)
1566 case MOVETYPE_FAKEPUSH:
1567 SV_Physics_Pusher (ent);
1570 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1571 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1574 case MOVETYPE_FOLLOW:
1575 SV_Physics_Follow (ent);
1577 case MOVETYPE_NOCLIP:
1578 if (SV_RunThink(ent))
1581 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1582 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1586 SV_Physics_Step (ent);
1589 if (SV_RunThink (ent))
1591 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1592 SV_AddGravity (ent);
1593 SV_CheckStuck (ent);
1598 case MOVETYPE_BOUNCE:
1599 case MOVETYPE_BOUNCEMISSILE:
1600 case MOVETYPE_FLYMISSILE:
1602 if (SV_RunThink (ent))
1603 SV_Physics_Toss (ent);
1606 if (SV_RunThink (ent))
1608 SV_CheckWater (ent);
1613 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1617 SV_CheckVelocity (ent);
1619 // call standard player post-think
1620 SV_LinkEdict (ent, true);
1622 SV_CheckVelocity (ent);
1624 prog->globals.server->time = sv.time;
1625 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1626 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1635 void SV_Physics (void)
1640 // let the progs know that a new frame has started
1641 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1642 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1643 prog->globals.server->time = sv.time;
1644 prog->globals.server->frametime = sv.frametime;
1645 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1648 // treat each object in turn
1651 // if force_retouch, relink all the entities
1652 if (prog->globals.server->force_retouch > 0)
1653 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1654 if (!ent->priv.server->free)
1655 SV_LinkEdict (ent, true); // force retouch even for stationary
1657 // run physics on the client entities
1658 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1660 if (!ent->priv.server->free)
1662 // don't do physics on disconnected clients, FrikBot relies on this
1663 if (!host_client->spawned)
1664 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1665 // don't run physics here if running asynchronously
1666 else if (!host_client->movesequence)
1667 SV_Physics_ClientEntity(ent);
1671 // run physics on all the non-client entities
1672 if (!sv_freezenonclients.integer)
1673 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1674 if (!ent->priv.server->free)
1675 SV_Physics_Entity(ent);
1677 if (prog->globals.server->force_retouch > 0)
1678 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1680 // LordHavoc: endframe support
1683 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1684 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1685 prog->globals.server->time = sv.time;
1686 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1689 // decrement prog->num_edicts if the highest number entities died
1690 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1692 if (!sv_freezenonclients.integer)
1693 sv.time += sv.frametime;
1697 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1702 vec3_t original_origin;
1703 vec3_t original_velocity;
1704 vec3_t original_angles;
1705 vec3_t original_avelocity;
1709 VectorCopy(tossent->fields.server->origin , original_origin );
1710 VectorCopy(tossent->fields.server->velocity , original_velocity );
1711 VectorCopy(tossent->fields.server->angles , original_angles );
1712 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1714 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1715 if (val != NULL && val->_float != 0)
1716 gravity = val->_float;
1719 gravity *= sv_gravity.value * 0.05;
1721 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1723 SV_CheckVelocity (tossent);
1724 tossent->fields.server->velocity[2] -= gravity;
1725 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1726 VectorScale (tossent->fields.server->velocity, 0.05, move);
1727 VectorAdd (tossent->fields.server->origin, move, end);
1728 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1729 VectorCopy (trace.endpos, tossent->fields.server->origin);
1731 if (trace.fraction < 1)
1735 VectorCopy(original_origin , tossent->fields.server->origin );
1736 VectorCopy(original_velocity , tossent->fields.server->velocity );
1737 VectorCopy(original_angles , tossent->fields.server->angles );
1738 VectorCopy(original_avelocity, tossent->fields.server->avelocity);