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_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
46 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
47 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
48 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
49 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
50 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
51 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
52 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
53 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
54 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
55 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
57 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)"};
58 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)"};
60 #define MOVE_EPSILON 0.01
62 void SV_Physics_Toss (prvm_edict_t *ent);
64 void SV_Phys_Init (void)
66 Cvar_RegisterVariable(&sv_stepheight);
67 Cvar_RegisterVariable(&sv_jumpstep);
68 Cvar_RegisterVariable(&sv_wallfriction);
69 Cvar_RegisterVariable(&sv_newflymove);
70 Cvar_RegisterVariable(&sv_freezenonclients);
72 Cvar_RegisterVariable(&sv_playerphysicsqc);
74 Cvar_RegisterVariable(&sv_sound_watersplash);
75 Cvar_RegisterVariable(&sv_sound_land);
82 returns true if the entity is in solid currently
85 static int SV_TestEntityPosition (prvm_edict_t *ent)
87 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
88 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
99 void SV_CheckAllEnts (void)
104 // see if any solid entities are inside the final position
105 check = PRVM_NEXT_EDICT(prog->edicts);
106 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
108 if (check->priv.server->free)
110 if (check->fields.server->movetype == MOVETYPE_PUSH
111 || check->fields.server->movetype == MOVETYPE_NONE
112 || check->fields.server->movetype == MOVETYPE_FOLLOW
113 || check->fields.server->movetype == MOVETYPE_NOCLIP)
116 if (SV_TestEntityPosition (check))
117 Con_Print("entity in invalid position\n");
126 void SV_CheckVelocity (prvm_edict_t *ent)
134 for (i=0 ; i<3 ; i++)
136 if (IS_NAN(ent->fields.server->velocity[i]))
138 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
139 ent->fields.server->velocity[i] = 0;
141 if (IS_NAN(ent->fields.server->origin[i]))
143 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
144 ent->fields.server->origin[i] = 0;
148 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
149 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
150 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
152 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
153 ent->fields.server->velocity[0] *= wishspeed;
154 ent->fields.server->velocity[1] *= wishspeed;
155 ent->fields.server->velocity[2] *= wishspeed;
163 Runs thinking code if time. There is some play in the exact time the think
164 function will be called, because it is called before any movement is done
165 in a frame. Not used for pushmove objects, because they must be exact.
166 Returns false if the entity removed itself.
169 qboolean SV_RunThink (prvm_edict_t *ent)
173 thinktime = ent->fields.server->nextthink;
174 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
177 // don't let things stay in the past.
178 // it is possible to start that way by a trigger with a local time.
179 if (thinktime < sv.time)
182 ent->fields.server->nextthink = 0;
183 prog->globals.server->time = thinktime;
184 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
185 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
186 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
187 return !ent->priv.server->free;
194 Two entities have touched, so run their touch functions
197 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
199 int old_self, old_other;
200 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
203 old_self = prog->globals.server->self;
204 old_other = prog->globals.server->other;
206 prog->globals.server->time = sv.time;
207 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
209 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
210 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
211 prog->globals.server->trace_allsolid = trace->allsolid;
212 prog->globals.server->trace_startsolid = trace->startsolid;
213 prog->globals.server->trace_fraction = trace->fraction;
214 prog->globals.server->trace_inwater = trace->inwater;
215 prog->globals.server->trace_inopen = trace->inopen;
216 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
217 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
218 prog->globals.server->trace_plane_dist = trace->plane.dist;
220 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
222 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
223 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
224 val->_float = trace->startsupercontents;
225 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
226 val->_float = trace->hitsupercontents;
227 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
228 val->_float = trace->hitq3surfaceflags;
229 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
231 if (trace->hittexture)
233 char *s = VM_GetTempString();
234 strlcpy(s, trace->hittexture->name, VM_STRINGTEMP_LENGTH);
235 val->string = PRVM_SetEngineString(s);
240 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
243 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
245 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
246 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
247 prog->globals.server->trace_allsolid = false;
248 prog->globals.server->trace_startsolid = false;
249 prog->globals.server->trace_fraction = 1;
250 prog->globals.server->trace_inwater = false;
251 prog->globals.server->trace_inopen = true;
252 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
253 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
254 prog->globals.server->trace_plane_dist = 0;
255 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
256 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
258 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
260 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
262 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
264 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
267 prog->globals.server->self = old_self;
268 prog->globals.server->other = old_other;
276 Slide off of the impacting object
277 returns the blocked flags (1 = floor, 2 = step / wall)
280 #define STOP_EPSILON 0.1
281 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
286 backoff = -DotProduct (in, normal) * overbounce;
287 VectorMA(in, backoff, normal, out);
289 for (i = 0;i < 3;i++)
290 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
299 The basic solid body movement clip that slides along multiple planes
300 Returns the clipflags if the velocity was modified (hit something solid)
304 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
307 // LordHavoc: increased from 5 to 32
308 #define MAX_CLIP_PLANES 32
309 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
311 int blocked, bumpcount;
312 int i, j, impact, numplanes;
314 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
317 VectorCopy(ent->fields.server->velocity, original_velocity);
318 VectorCopy(ent->fields.server->velocity, primal_velocity);
321 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
323 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
326 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
327 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
329 //if (trace.fraction < 0.002)
334 VectorCopy(ent->fields.server->origin, start);
335 start[2] += 3;//0.03125;
336 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
337 end[2] += 3;//0.03125;
338 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
339 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)))
341 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
347 for (i = 0;i < numplanes;i++)
349 VectorCopy(ent->fields.server->origin, start);
350 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
351 VectorMA(start, 3, planes[i], start);
352 VectorMA(end, 3, planes[i], end);
353 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
354 if (trace.fraction < testtrace.fraction)
357 VectorCopy(start, ent->fields.server->origin);
362 // VectorAdd(ent->fields.server->origin, planes[j], start);
368 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);
369 if (trace.fraction < 1)
370 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
374 if (trace.bmodelstartsolid)
376 // LordHavoc: note: this code is what makes entities stick in place
377 // if embedded in world only (you can walk through other objects if
379 // entity is trapped in another solid
380 VectorClear(ent->fields.server->velocity);
384 // break if it moved the entire distance
385 if (trace.fraction == 1)
387 VectorCopy(trace.endpos, ent->fields.server->origin);
393 Con_Printf ("SV_FlyMove: !trace.ent");
394 trace.ent = prog->edicts;
397 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
401 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
405 if (trace.plane.normal[2])
407 if (trace.plane.normal[2] > 0.7)
411 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
412 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
419 // save the trace for player extrafriction
421 VectorCopy(trace.plane.normal, stepnormal);
424 if (trace.fraction >= 0.001)
426 // actually covered some distance
427 VectorCopy(trace.endpos, ent->fields.server->origin);
428 VectorCopy(ent->fields.server->velocity, original_velocity);
432 // run the impact function
435 SV_Impact(ent, &trace);
437 // break if removed by the impact function
438 if (ent->priv.server->free)
442 time_left *= 1 - trace.fraction;
444 // clipped to another plane
445 if (numplanes >= MAX_CLIP_PLANES)
447 // this shouldn't really happen
448 VectorClear(ent->fields.server->velocity);
454 for (i = 0;i < numplanes;i++)
455 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
459 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
464 VectorCopy(trace.plane.normal, planes[numplanes]);
467 if (sv_newflymove.integer)
468 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
471 // modify original_velocity so it parallels all of the clip planes
472 for (i = 0;i < numplanes;i++)
474 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
475 for (j = 0;j < numplanes;j++)
480 if (DotProduct(new_velocity, planes[j]) < 0)
490 // go along this plane
491 VectorCopy(new_velocity, ent->fields.server->velocity);
495 // go along the crease
498 VectorClear(ent->fields.server->velocity);
502 CrossProduct(planes[0], planes[1], dir);
503 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
504 VectorNormalize(dir);
505 d = DotProduct(dir, ent->fields.server->velocity);
506 VectorScale(dir, d, ent->fields.server->velocity);
510 // if current velocity is against the original velocity,
511 // stop dead to avoid tiny occilations in sloping corners
512 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
514 VectorClear(ent->fields.server->velocity);
519 //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]);
522 if ((blocked & 1) == 0 && bumpcount > 1)
524 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
525 // flag ONGROUND if there's ground under it
526 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
538 void SV_AddGravity (prvm_edict_t *ent)
543 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
544 if (val!=0 && val->_float)
545 ent_gravity = val->_float;
548 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
553 ===============================================================================
557 ===============================================================================
564 Does not change the entities velocity at all
567 trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push)
573 VectorAdd (ent->fields.server->origin, push, end);
575 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
577 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
578 type = MOVE_NOMONSTERS; // only clip against bmodels
582 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
584 VectorCopy (trace.endpos, ent->fields.server->origin);
585 SV_LinkEdict (ent, true);
587 if (trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
588 SV_Impact (ent, &trace);
599 void SV_PushMove (prvm_edict_t *pusher, float movetime)
602 float savesolid, movetime2, pushltime;
603 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
605 int numcheckentities;
606 static prvm_edict_t *checkentities[MAX_EDICTS];
607 model_t *pushermodel;
610 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])
612 pusher->fields.server->ltime += movetime;
616 switch ((int) pusher->fields.server->solid)
618 // LordHavoc: valid pusher types
622 case SOLID_CORPSE: // LordHavoc: this would be weird...
624 // LordHavoc: no collisions
627 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
628 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
629 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
630 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
631 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
632 pusher->fields.server->ltime += movetime;
633 SV_LinkEdict (pusher, false);
636 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
639 index = (int) pusher->fields.server->modelindex;
640 if (index < 1 || index >= MAX_MODELS)
642 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
645 pushermodel = sv.models[index];
647 movetime2 = movetime;
648 VectorScale(pusher->fields.server->velocity, movetime2, move1);
649 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
650 if (moveangle[0] || moveangle[2])
652 for (i = 0;i < 3;i++)
656 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
657 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
661 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
662 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
666 else if (moveangle[1])
668 for (i = 0;i < 3;i++)
672 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
673 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
677 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
678 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
684 for (i = 0;i < 3;i++)
688 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
689 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
693 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
694 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
699 VectorNegate (moveangle, a);
700 AngleVectorsFLU (a, forward, left, up);
702 VectorCopy (pusher->fields.server->origin, pushorig);
703 VectorCopy (pusher->fields.server->angles, pushang);
704 pushltime = pusher->fields.server->ltime;
706 // move the pusher to its final position
708 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
709 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
710 pusher->fields.server->ltime += movetime;
711 SV_LinkEdict (pusher, false);
713 savesolid = pusher->fields.server->solid;
715 // see if any solid entities are inside the final position
718 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
719 for (e = 0;e < numcheckentities;e++)
721 prvm_edict_t *check = checkentities[e];
722 if (check->fields.server->movetype == MOVETYPE_NONE
723 || check->fields.server->movetype == MOVETYPE_PUSH
724 || check->fields.server->movetype == MOVETYPE_FOLLOW
725 || check->fields.server->movetype == MOVETYPE_NOCLIP
726 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
729 // if the entity is standing on the pusher, it will definitely be moved
730 if (!(((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher))
732 // if the entity is not inside the pusher's final position, leave it alone
733 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)
735 // remove the onground flag for non-players
736 if (check->fields.server->movetype != MOVETYPE_WALK)
737 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
741 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
744 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
745 org2[0] = DotProduct (org, forward);
746 org2[1] = DotProduct (org, left);
747 org2[2] = DotProduct (org, up);
748 VectorSubtract (org2, org, move);
749 VectorAdd (move, move1, move);
752 VectorCopy (move1, move);
754 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
755 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
756 sv.moved_edicts[num_moved++] = check;
758 // try moving the contacted entity
759 pusher->fields.server->solid = SOLID_NOT;
760 trace = SV_PushEntity (check, move);
761 // FIXME: turn players specially
762 check->fields.server->angles[1] += trace.fraction * moveangle[1];
763 pusher->fields.server->solid = savesolid; // was SOLID_BSP
765 // if it is still inside the pusher, block
766 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)
768 // try moving the contacted entity a tiny bit further to account for precision errors
770 pusher->fields.server->solid = SOLID_NOT;
771 VectorScale(move, 1.1, move2);
772 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
773 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
774 SV_PushEntity (check, move2);
775 pusher->fields.server->solid = savesolid;
776 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)
778 // try moving the contacted entity a tiny bit less to account for precision errors
779 pusher->fields.server->solid = SOLID_NOT;
780 VectorScale(move, 0.9, move2);
781 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
782 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
783 SV_PushEntity (check, move2);
784 pusher->fields.server->solid = savesolid;
785 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)
787 // still inside pusher, so it's really blocked
790 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
792 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
795 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
796 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
800 VectorCopy (pushorig, pusher->fields.server->origin);
801 VectorCopy (pushang, pusher->fields.server->angles);
802 pusher->fields.server->ltime = pushltime;
803 SV_LinkEdict (pusher, false);
805 // move back any entities we already moved
806 for (i = 0;i < num_moved;i++)
808 prvm_edict_t *ed = sv.moved_edicts[i];
809 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
810 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
811 SV_LinkEdict (ed, false);
814 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
815 if (pusher->fields.server->blocked)
817 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
818 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
819 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
826 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
827 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
828 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
837 void SV_Physics_Pusher (prvm_edict_t *ent)
839 float thinktime, oldltime, movetime;
841 oldltime = ent->fields.server->ltime;
843 thinktime = ent->fields.server->nextthink;
844 if (thinktime < ent->fields.server->ltime + sv.frametime)
846 movetime = thinktime - ent->fields.server->ltime;
851 movetime = sv.frametime;
854 // advances ent->fields.server->ltime if not blocked
855 SV_PushMove (ent, movetime);
857 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
859 ent->fields.server->nextthink = 0;
860 prog->globals.server->time = sv.time;
861 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
862 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
863 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
869 ===============================================================================
873 ===============================================================================
880 This is a big hack to try and fix the rare case of getting stuck in the world
884 void SV_CheckStuck (prvm_edict_t *ent)
889 if (!SV_TestEntityPosition(ent))
891 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
895 VectorCopy (ent->fields.server->origin, org);
896 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
897 if (!SV_TestEntityPosition(ent))
899 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
900 SV_LinkEdict (ent, true);
904 for (z=-1 ; z< 18 ; z++)
905 for (i=-1 ; i <= 1 ; i++)
906 for (j=-1 ; j <= 1 ; j++)
908 ent->fields.server->origin[0] = org[0] + i;
909 ent->fields.server->origin[1] = org[1] + j;
910 ent->fields.server->origin[2] = org[2] + z;
911 if (!SV_TestEntityPosition(ent))
913 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);
914 SV_LinkEdict (ent, true);
919 VectorCopy (org, ent->fields.server->origin);
920 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
923 static void SV_UnstickEntity (prvm_edict_t *ent)
928 // if not stuck in a bmodel, just return
929 if (!SV_TestEntityPosition(ent))
932 VectorCopy (ent->fields.server->origin, org);
934 for (z=-1 ; z< 18 ; z += 6)
935 for (i=-1 ; i <= 1 ; i++)
936 for (j=-1 ; j <= 1 ; j++)
938 ent->fields.server->origin[0] = org[0] + i;
939 ent->fields.server->origin[1] = org[1] + j;
940 ent->fields.server->origin[2] = org[2] + z;
941 if (!SV_TestEntityPosition(ent))
943 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);
944 SV_LinkEdict (ent, true);
949 VectorCopy (org, ent->fields.server->origin);
950 Con_DPrintf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
959 qboolean SV_CheckWater (prvm_edict_t *ent)
964 point[0] = ent->fields.server->origin[0];
965 point[1] = ent->fields.server->origin[1];
966 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
968 ent->fields.server->waterlevel = 0;
969 ent->fields.server->watertype = CONTENTS_EMPTY;
970 cont = SV_PointSuperContents(point);
971 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
973 ent->fields.server->watertype = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
974 ent->fields.server->waterlevel = 1;
975 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
976 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
978 ent->fields.server->waterlevel = 2;
979 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
980 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
981 ent->fields.server->waterlevel = 3;
985 return ent->fields.server->waterlevel > 1;
994 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
997 vec3_t forward, into, side;
999 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1000 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1002 // cut the tangential velocity
1003 i = DotProduct (stepnormal, ent->fields.server->velocity);
1004 VectorScale (stepnormal, i, into);
1005 VectorSubtract (ent->fields.server->velocity, into, side);
1006 ent->fields.server->velocity[0] = side[0] * (1 + d);
1007 ent->fields.server->velocity[1] = side[1] * (1 + d);
1012 =====================
1015 Player has come to a dead stop, possibly due to the problem with limited
1016 float precision at some angle joins in the BSP hull.
1018 Try fixing by pushing one pixel in each direction.
1020 This is a hack, but in the interest of good gameplay...
1021 ======================
1023 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1028 VectorCopy (ent->fields.server->origin, oldorg);
1031 for (i=0 ; i<8 ; i++)
1033 // try pushing a little in an axial direction
1036 case 0: dir[0] = 2; dir[1] = 0; break;
1037 case 1: dir[0] = 0; dir[1] = 2; break;
1038 case 2: dir[0] = -2; dir[1] = 0; break;
1039 case 3: dir[0] = 0; dir[1] = -2; break;
1040 case 4: dir[0] = 2; dir[1] = 2; break;
1041 case 5: dir[0] = -2; dir[1] = 2; break;
1042 case 6: dir[0] = 2; dir[1] = -2; break;
1043 case 7: dir[0] = -2; dir[1] = -2; break;
1046 SV_PushEntity (ent, dir);
1048 // retry the original move
1049 ent->fields.server->velocity[0] = oldvel[0];
1050 ent->fields.server->velocity[1] = oldvel[1];
1051 ent->fields.server->velocity[2] = 0;
1052 clip = SV_FlyMove (ent, 0.1, NULL);
1054 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1055 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1057 Con_DPrint("TryUnstick - success.\n");
1061 // go back to the original pos and try again
1062 VectorCopy (oldorg, ent->fields.server->origin);
1066 VectorClear (ent->fields.server->velocity);
1067 Con_DPrint("TryUnstick - failure.\n");
1072 =====================
1075 Only used by players
1076 ======================
1078 void SV_WalkMove (prvm_edict_t *ent)
1080 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1081 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1084 SV_CheckVelocity(ent);
1086 // do a regular slide move unless it looks like you ran into a step
1087 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1088 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1090 VectorCopy (ent->fields.server->origin, start_origin);
1091 VectorCopy (ent->fields.server->velocity, start_velocity);
1093 clip = SV_FlyMove (ent, sv.frametime, NULL);
1095 SV_CheckVelocity(ent);
1097 VectorCopy(ent->fields.server->origin, originalmove_origin);
1098 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1099 originalmove_clip = clip;
1100 originalmove_flags = (int)ent->fields.server->flags;
1101 originalmove_groundentity = ent->fields.server->groundentity;
1103 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1106 if (sv_nostep.integer)
1109 // if move didn't block on a step, return
1112 // if move was not trying to move into the step, return
1113 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1116 if (ent->fields.server->movetype != MOVETYPE_FLY)
1118 // return if gibbed by a trigger
1119 if (ent->fields.server->movetype != MOVETYPE_WALK)
1122 // only step up while jumping if that is enabled
1123 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1124 if (!oldonground && ent->fields.server->waterlevel == 0)
1128 // try moving up and forward to go up a step
1129 // back to start pos
1130 VectorCopy (start_origin, ent->fields.server->origin);
1131 VectorCopy (start_velocity, ent->fields.server->velocity);
1134 VectorClear (upmove);
1135 upmove[2] = sv_stepheight.value;
1136 // FIXME: don't link?
1137 SV_PushEntity(ent, upmove);
1140 ent->fields.server->velocity[2] = 0;
1141 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1142 ent->fields.server->velocity[2] += start_velocity[2];
1144 SV_CheckVelocity(ent);
1146 // check for stuckness, possibly due to the limited precision of floats
1147 // in the clipping hulls
1149 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1150 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1152 //Con_Printf("wall\n");
1153 // stepping up didn't make any progress, revert to original move
1154 VectorCopy(originalmove_origin, ent->fields.server->origin);
1155 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1156 //clip = originalmove_clip;
1157 ent->fields.server->flags = originalmove_flags;
1158 ent->fields.server->groundentity = originalmove_groundentity;
1159 // now try to unstick if needed
1160 //clip = SV_TryUnstick (ent, oldvel);
1164 //Con_Printf("step - ");
1166 // extra friction based on view angle
1167 if (clip & 2 && sv_wallfriction.integer)
1168 SV_WallFriction (ent, stepnormal);
1170 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1171 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)))
1175 VectorClear (downmove);
1176 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1177 // FIXME: don't link?
1178 downtrace = SV_PushEntity (ent, downmove);
1180 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1182 // this has been disabled so that you can't jump when you are stepping
1183 // up while already jumping (also known as the Quake2 stair jump bug)
1185 // LordHavoc: disabled this check so you can walk on monsters/players
1186 //if (ent->fields.server->solid == SOLID_BSP)
1188 //Con_Printf("onground\n");
1189 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1190 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1196 //Con_Printf("slope\n");
1197 // if the push down didn't end up on good ground, use the move without
1198 // the step up. This happens near wall / slope combinations, and can
1199 // cause the player to hop up higher on a slope too steep to climb
1200 VectorCopy(originalmove_origin, ent->fields.server->origin);
1201 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1202 //clip = originalmove_clip;
1203 ent->fields.server->flags = originalmove_flags;
1204 ent->fields.server->groundentity = originalmove_groundentity;
1207 SV_CheckVelocity(ent);
1210 //============================================================================
1216 Entities that are "stuck" to another entity
1219 void SV_Physics_Follow (prvm_edict_t *ent)
1221 vec3_t vf, vr, vu, angles, v;
1225 if (!SV_RunThink (ent))
1228 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1229 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1230 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])
1232 // quick case for no rotation
1233 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1237 angles[0] = -ent->fields.server->punchangle[0];
1238 angles[1] = ent->fields.server->punchangle[1];
1239 angles[2] = ent->fields.server->punchangle[2];
1240 AngleVectors (angles, vf, vr, vu);
1241 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];
1242 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];
1243 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];
1244 angles[0] = -e->fields.server->angles[0];
1245 angles[1] = e->fields.server->angles[1];
1246 angles[2] = e->fields.server->angles[2];
1247 AngleVectors (angles, vf, vr, vu);
1248 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1249 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1250 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1252 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1253 SV_LinkEdict (ent, true);
1257 ==============================================================================
1261 ==============================================================================
1266 SV_CheckWaterTransition
1270 void SV_CheckWaterTransition (prvm_edict_t *ent)
1273 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1274 if (!ent->fields.server->watertype)
1276 // just spawned here
1277 ent->fields.server->watertype = cont;
1278 ent->fields.server->waterlevel = 1;
1282 // check if the entity crossed into or out of water
1283 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1284 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1286 if (cont <= CONTENTS_WATER)
1288 ent->fields.server->watertype = cont;
1289 ent->fields.server->waterlevel = 1;
1293 ent->fields.server->watertype = CONTENTS_EMPTY;
1294 ent->fields.server->waterlevel = 0;
1302 Toss, bounce, and fly movement. When onground, do nothing.
1305 void SV_Physics_Toss (prvm_edict_t *ent)
1310 // if onground, return without moving
1311 if ((int)ent->fields.server->flags & FL_ONGROUND)
1313 // don't stick to ground if onground and moving upward
1314 if (ent->fields.server->velocity[2] >= (1.0 / 32.0))
1315 ent->fields.server->flags -= FL_ONGROUND;
1318 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1319 if (ground->fields.server->solid == SOLID_BSP || !sv_gameplayfix_noairborncorpse.integer)
1321 // if ent was supported by a brush model on previous frame,
1322 // and groundentity is now freed, set groundentity to 0 (floating)
1323 if (ent->priv.server->suspendedinairflag && ground->priv.server->free)
1325 // leave it suspended in the air
1326 ent->fields.server->groundentity = 0;
1331 ent->priv.server->suspendedinairflag = false;
1333 SV_CheckVelocity (ent);
1336 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1337 SV_AddGravity (ent);
1340 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1343 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1344 trace = SV_PushEntity (ent, move);
1345 if (ent->priv.server->free)
1347 if (trace.bmodelstartsolid)
1349 // try to unstick the entity
1350 SV_UnstickEntity(ent);
1351 trace = SV_PushEntity (ent, move);
1352 if (ent->priv.server->free)
1356 if (trace.fraction < 1)
1358 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1360 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1361 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1363 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1366 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1367 // LordHavoc: fixed grenades not bouncing when fired down a slope
1368 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1370 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1371 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1373 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1374 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1375 VectorClear (ent->fields.server->velocity);
1376 VectorClear (ent->fields.server->avelocity);
1379 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1383 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1385 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1386 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1387 VectorClear (ent->fields.server->velocity);
1388 VectorClear (ent->fields.server->avelocity);
1391 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1396 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1397 if (trace.plane.normal[2] > 0.7)
1399 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1400 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1401 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1402 ent->priv.server->suspendedinairflag = true;
1403 VectorClear (ent->fields.server->velocity);
1404 VectorClear (ent->fields.server->avelocity);
1407 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1411 // check for in water
1412 SV_CheckWaterTransition (ent);
1416 ===============================================================================
1420 ===============================================================================
1427 Monsters freefall when they don't have a ground entity, otherwise
1428 all movement is done with discrete steps.
1430 This is also used for objects that have become still on the ground, but
1431 will fall if the floor is pulled out from under them.
1434 void SV_Physics_Step (prvm_edict_t *ent)
1436 int flags = (int)ent->fields.server->flags;
1437 // don't fall at all if fly/swim
1438 if (!(flags & (FL_FLY | FL_SWIM)))
1440 if (flags & FL_ONGROUND)
1442 // freefall if onground and moving upward
1443 // freefall if not standing on a world surface (it may be a lift)
1444 prvm_edict_t *ground = PRVM_PROG_TO_EDICT(ent->fields.server->groundentity);
1445 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) || (ground->fields.server->solid != SOLID_BSP && sv_gameplayfix_noairborncorpse.integer))
1447 ent->fields.server->flags -= FL_ONGROUND;
1449 SV_CheckVelocity(ent);
1450 SV_FlyMove(ent, sv.frametime, NULL);
1451 SV_LinkEdict(ent, true);
1456 // freefall if not onground
1457 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1460 SV_CheckVelocity(ent);
1461 SV_FlyMove(ent, sv.frametime, NULL);
1462 SV_LinkEdict(ent, true);
1465 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1466 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1473 SV_CheckWaterTransition(ent);
1476 //============================================================================
1478 static void SV_Physics_Entity (prvm_edict_t *ent)
1480 // don't run a move on newly spawned projectiles as it messes up movement
1481 // interpolation and rocket trails
1482 qboolean runmove = ent->priv.server->move;
1483 ent->priv.server->move = true;
1484 switch ((int) ent->fields.server->movetype)
1487 case MOVETYPE_FAKEPUSH:
1488 SV_Physics_Pusher (ent);
1491 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1492 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1495 case MOVETYPE_FOLLOW:
1496 SV_Physics_Follow (ent);
1498 case MOVETYPE_NOCLIP:
1499 if (SV_RunThink(ent))
1502 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1503 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1505 SV_LinkEdict(ent, false);
1508 SV_Physics_Step (ent);
1511 if (SV_RunThink (ent))
1513 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1514 SV_AddGravity (ent);
1515 SV_CheckStuck (ent);
1517 SV_LinkEdict (ent, true);
1521 case MOVETYPE_BOUNCE:
1522 case MOVETYPE_BOUNCEMISSILE:
1523 case MOVETYPE_FLYMISSILE:
1526 if (SV_RunThink (ent) && runmove)
1527 SV_Physics_Toss (ent);
1530 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1535 void SV_ApplyClientMove (void);
1536 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1538 SV_ApplyClientMove();
1539 // make sure the velocity is sane (not a NaN)
1540 SV_CheckVelocity(ent);
1541 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1542 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1544 prog->globals.server->time = sv.time;
1545 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1546 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1550 // make sure the velocity is sane (not a NaN)
1551 SV_CheckVelocity(ent);
1552 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1553 // player_run/player_stand1 does not horribly malfunction if the
1554 // velocity becomes a number that is both == 0 and != 0
1555 // (sounds to me like NaN but to be absolutely safe...)
1556 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1557 VectorClear(ent->fields.server->velocity);
1558 // call standard client pre-think
1559 prog->globals.server->time = sv.time;
1560 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1561 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1562 SV_CheckVelocity (ent);
1564 switch ((int) ent->fields.server->movetype)
1567 case MOVETYPE_FAKEPUSH:
1568 SV_Physics_Pusher (ent);
1571 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1572 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1575 case MOVETYPE_FOLLOW:
1576 SV_Physics_Follow (ent);
1578 case MOVETYPE_NOCLIP:
1579 if (SV_RunThink(ent))
1582 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1583 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1587 SV_Physics_Step (ent);
1590 if (SV_RunThink (ent))
1592 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1593 SV_AddGravity (ent);
1594 SV_CheckStuck (ent);
1599 case MOVETYPE_BOUNCE:
1600 case MOVETYPE_BOUNCEMISSILE:
1601 case MOVETYPE_FLYMISSILE:
1603 if (SV_RunThink (ent))
1604 SV_Physics_Toss (ent);
1607 if (SV_RunThink (ent))
1609 SV_CheckWater (ent);
1614 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1618 SV_CheckVelocity (ent);
1620 // call standard player post-think
1621 SV_LinkEdict (ent, true);
1623 SV_CheckVelocity (ent);
1625 prog->globals.server->time = sv.time;
1626 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1627 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1636 void SV_Physics (void)
1641 // let the progs know that a new frame has started
1642 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1643 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1644 prog->globals.server->time = sv.time;
1645 prog->globals.server->frametime = sv.frametime;
1646 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1649 // treat each object in turn
1652 // if force_retouch, relink all the entities
1653 if (prog->globals.server->force_retouch > 0)
1654 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1655 if (!ent->priv.server->free)
1656 SV_LinkEdict (ent, true); // force retouch even for stationary
1658 // run physics on the client entities
1659 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1661 if (!ent->priv.server->free)
1663 // don't do physics on disconnected clients, FrikBot relies on this
1664 if (!host_client->spawned)
1665 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1666 // don't run physics here if running asynchronously
1667 else if (!host_client->movesequence)
1668 SV_Physics_ClientEntity(ent);
1672 // run physics on all the non-client entities
1673 if (!sv_freezenonclients.integer)
1674 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1675 if (!ent->priv.server->free)
1676 SV_Physics_Entity(ent);
1678 if (prog->globals.server->force_retouch > 0)
1679 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1681 // LordHavoc: endframe support
1684 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1685 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1686 prog->globals.server->time = sv.time;
1687 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1690 // decrement prog->num_edicts if the highest number entities died
1691 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1693 if (!sv_freezenonclients.integer)
1694 sv.time += sv.frametime;
1698 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1703 vec3_t original_origin;
1704 vec3_t original_velocity;
1705 vec3_t original_angles;
1706 vec3_t original_avelocity;
1710 VectorCopy(tossent->fields.server->origin , original_origin );
1711 VectorCopy(tossent->fields.server->velocity , original_velocity );
1712 VectorCopy(tossent->fields.server->angles , original_angles );
1713 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1715 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1716 if (val != NULL && val->_float != 0)
1717 gravity = val->_float;
1720 gravity *= sv_gravity.value * 0.05;
1722 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1724 SV_CheckVelocity (tossent);
1725 tossent->fields.server->velocity[2] -= gravity;
1726 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1727 VectorScale (tossent->fields.server->velocity, 0.05, move);
1728 VectorAdd (tossent->fields.server->origin, move, end);
1729 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1730 VectorCopy (trace.endpos, tossent->fields.server->origin);
1732 if (trace.fraction < 1)
1736 VectorCopy(original_origin , tossent->fields.server->origin );
1737 VectorCopy(original_velocity , tossent->fields.server->velocity );
1738 VectorCopy(original_angles , tossent->fields.server->angles );
1739 VectorCopy(original_avelocity, tossent->fields.server->avelocity);