2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 pushmove objects do not obey gravity, and do not interact with each other or trigger fields, but block normal movement and push normal objects when they move.
29 onground is set for toss objects when they come to a complete rest. it is set for steping or walking objects
31 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33 corpses are SOLID_NOT and MOVETYPE_TOSS
34 crates are SOLID_BBOX and MOVETYPE_TOSS
35 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
38 solid_edge items only clip against bsp models.
42 cvar_t sv_friction = {CVAR_NOTIFY, "sv_friction","4", "how fast you slow down"};
43 cvar_t sv_waterfriction = {CVAR_NOTIFY, "sv_waterfriction","-1", "how fast you slow down, if less than 0 the sv_friction variable is used instead"};
44 cvar_t sv_stopspeed = {CVAR_NOTIFY, "sv_stopspeed","100", "how fast you come to a complete stop"};
45 cvar_t sv_gravity = {CVAR_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
46 cvar_t sv_maxvelocity = {CVAR_NOTIFY, "sv_maxvelocity","2000", "universal speed limit on all entities"};
47 cvar_t sv_nostep = {CVAR_NOTIFY, "sv_nostep","0", "prevents MOVETYPE_STEP entities (monsters) from moving"};
48 cvar_t sv_stepheight = {CVAR_NOTIFY, "sv_stepheight", "18", "how high you can step up (TW_SV_STEPCONTROL extension)"};
49 cvar_t sv_jumpstep = {CVAR_NOTIFY, "sv_jumpstep", "0", "whether you can step up while jumping (sv_gameplayfix_stepwhilejumping must also be 1)"};
50 cvar_t sv_wallfriction = {CVAR_NOTIFY, "sv_wallfriction", "1", "how much you slow down when sliding along a wall"};
51 cvar_t sv_newflymove = {CVAR_NOTIFY, "sv_newflymove", "0", "enables simpler/buggier player physics (not recommended)"};
52 cvar_t sv_freezenonclients = {CVAR_NOTIFY, "sv_freezenonclients", "0", "freezes time, except for players, allowing you to walk around and take screenshots of explosions"};
53 cvar_t sv_playerphysicsqc = {CVAR_NOTIFY, "sv_playerphysicsqc", "1", "enables QuakeC function to override player physics"};
55 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)"};
56 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)"};
58 // TODO: move this extern to server.h
59 extern cvar_t sv_clmovement_waitforinput;
61 #define MOVE_EPSILON 0.01
63 void SV_Physics_Toss (prvm_edict_t *ent);
65 void SV_Phys_Init (void)
67 Cvar_RegisterVariable(&sv_stepheight);
68 Cvar_RegisterVariable(&sv_jumpstep);
69 Cvar_RegisterVariable(&sv_wallfriction);
70 Cvar_RegisterVariable(&sv_newflymove);
71 Cvar_RegisterVariable(&sv_freezenonclients);
73 Cvar_RegisterVariable(&sv_playerphysicsqc);
75 Cvar_RegisterVariable(&sv_sound_watersplash);
76 Cvar_RegisterVariable(&sv_sound_land);
83 returns true if the entity is in solid currently
86 static int SV_TestEntityPosition (prvm_edict_t *ent)
88 trace_t trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, ent->fields.server->origin, MOVE_NOMONSTERS, ent);
89 if (trace.startsupercontents & SUPERCONTENTS_SOLID)
93 if (sv.worldmodel->brushq1.numclipnodes && !VectorCompare(ent->fields.server->mins, ent->fields.server->maxs))
95 // q1bsp/hlbsp use hulls and if the entity does not exactly match
96 // a hull size it is incorrectly tested, so this code tries to
97 // 'fix' it slightly...
100 VectorAdd(ent->fields.server->origin, ent->fields.server->mins, m1);
101 VectorAdd(ent->fields.server->origin, ent->fields.server->maxs, m2);
102 VectorSubtract(m2, m1, s);
103 #define EPSILON (1.0f / 32.0f)
104 if (s[0] >= EPSILON*2) {m1[0] += EPSILON;m2[0] -= EPSILON;}
105 if (s[1] >= EPSILON*2) {m1[1] += EPSILON;m2[1] -= EPSILON;}
106 if (s[2] >= EPSILON*2) {m1[2] += EPSILON;m2[2] -= EPSILON;}
107 for (i = 0;i < 8;i++)
109 v[0] = (i & 1) ? m2[0] : m1[0];
110 v[1] = (i & 2) ? m2[1] : m1[1];
111 v[2] = (i & 4) ? m2[2] : m1[2];
112 if (SV_PointSuperContents(v) & SUPERCONTENTS_SOLID)
125 void SV_CheckAllEnts (void)
130 // see if any solid entities are inside the final position
131 check = PRVM_NEXT_EDICT(prog->edicts);
132 for (e = 1;e < prog->num_edicts;e++, check = PRVM_NEXT_EDICT(check))
134 if (check->priv.server->free)
136 if (check->fields.server->movetype == MOVETYPE_PUSH
137 || check->fields.server->movetype == MOVETYPE_NONE
138 || check->fields.server->movetype == MOVETYPE_FOLLOW
139 || check->fields.server->movetype == MOVETYPE_NOCLIP)
142 if (SV_TestEntityPosition (check))
143 Con_Print("entity in invalid position\n");
147 // DRESK - Support for Entity Contents Transition Event
150 SV_CheckContentsTransition
152 returns true if entity had a valid contentstransition function call
155 int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
157 int bValidFunctionCall;
158 prvm_eval_t *contentstransition;
160 // Default Valid Function Call to False
161 bValidFunctionCall = false;
163 if(ent->fields.server->watertype != nContents)
164 { // Changed Contents
165 // Acquire Contents Transition Function from QC
166 contentstransition = PRVM_GETEDICTFIELDVALUE(ent, eval_contentstransition);
168 if(contentstransition->function)
169 { // Valid Function; Execute
170 // Assign Valid Function
171 bValidFunctionCall = true;
172 // Prepare Parameters (Original Contents, New Contents)
174 PRVM_G_FLOAT(OFS_PARM0) = ent->fields.server->watertype;
176 PRVM_G_FLOAT(OFS_PARM1) = nContents;
177 // Execute VM Function
178 PRVM_ExecuteProgram(contentstransition->function, "contentstransition: NULL function");
182 // Return if Function Call was Valid
183 return bValidFunctionCall;
192 void SV_CheckVelocity (prvm_edict_t *ent)
200 for (i=0 ; i<3 ; i++)
202 if (IS_NAN(ent->fields.server->velocity[i]))
204 Con_Printf("Got a NaN velocity on %s\n", PRVM_GetString(ent->fields.server->classname));
205 ent->fields.server->velocity[i] = 0;
207 if (IS_NAN(ent->fields.server->origin[i]))
209 Con_Printf("Got a NaN origin on %s\n", PRVM_GetString(ent->fields.server->classname));
210 ent->fields.server->origin[i] = 0;
214 // LordHavoc: max velocity fix, inspired by Maddes's source fixes, but this is faster
215 wishspeed = DotProduct(ent->fields.server->velocity, ent->fields.server->velocity);
216 if (wishspeed > sv_maxvelocity.value * sv_maxvelocity.value)
218 wishspeed = sv_maxvelocity.value / sqrt(wishspeed);
219 ent->fields.server->velocity[0] *= wishspeed;
220 ent->fields.server->velocity[1] *= wishspeed;
221 ent->fields.server->velocity[2] *= wishspeed;
229 Runs thinking code if time. There is some play in the exact time the think
230 function will be called, because it is called before any movement is done
231 in a frame. Not used for pushmove objects, because they must be exact.
232 Returns false if the entity removed itself.
235 qboolean SV_RunThink (prvm_edict_t *ent)
239 thinktime = ent->fields.server->nextthink;
240 if (thinktime <= 0 || thinktime > sv.time + sv.frametime)
243 // don't let things stay in the past.
244 // it is possible to start that way by a trigger with a local time.
245 if (thinktime < sv.time)
248 ent->fields.server->nextthink = 0;
249 prog->globals.server->time = thinktime;
250 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
251 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
252 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
253 return !ent->priv.server->free;
260 Two entities have touched, so run their touch functions
263 void SV_Impact (prvm_edict_t *e1, trace_t *trace)
265 int old_self, old_other;
266 prvm_edict_t *e2 = (prvm_edict_t *)trace->ent;
269 old_self = prog->globals.server->self;
270 old_other = prog->globals.server->other;
272 prog->globals.server->time = sv.time;
273 if (!e1->priv.server->free && !e2->priv.server->free && e1->fields.server->touch && e1->fields.server->solid != SOLID_NOT)
275 prog->globals.server->self = PRVM_EDICT_TO_PROG(e1);
276 prog->globals.server->other = PRVM_EDICT_TO_PROG(e2);
277 prog->globals.server->trace_allsolid = trace->allsolid;
278 prog->globals.server->trace_startsolid = trace->startsolid;
279 prog->globals.server->trace_fraction = trace->fraction;
280 prog->globals.server->trace_inwater = trace->inwater;
281 prog->globals.server->trace_inopen = trace->inopen;
282 VectorCopy (trace->endpos, prog->globals.server->trace_endpos);
283 VectorCopy (trace->plane.normal, prog->globals.server->trace_plane_normal);
284 prog->globals.server->trace_plane_dist = trace->plane.dist;
286 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace->ent);
288 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts);
289 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
290 val->_float = trace->startsupercontents;
291 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
292 val->_float = trace->hitsupercontents;
293 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
294 val->_float = trace->hitq3surfaceflags;
295 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
297 if (trace->hittexture)
298 val->string = PRVM_SetTempString(trace->hittexture->name);
302 PRVM_ExecuteProgram (e1->fields.server->touch, "QC function self.touch is missing");
305 if (!e1->priv.server->free && !e2->priv.server->free && e2->fields.server->touch && e2->fields.server->solid != SOLID_NOT)
307 prog->globals.server->self = PRVM_EDICT_TO_PROG(e2);
308 prog->globals.server->other = PRVM_EDICT_TO_PROG(e1);
309 prog->globals.server->trace_allsolid = false;
310 prog->globals.server->trace_startsolid = false;
311 prog->globals.server->trace_fraction = 1;
312 prog->globals.server->trace_inwater = false;
313 prog->globals.server->trace_inopen = true;
314 VectorCopy (e2->fields.server->origin, prog->globals.server->trace_endpos);
315 VectorSet (prog->globals.server->trace_plane_normal, 0, 0, 1);
316 prog->globals.server->trace_plane_dist = 0;
317 prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(e1);
318 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents)))
320 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents)))
322 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags)))
324 if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename)))
326 PRVM_ExecuteProgram (e2->fields.server->touch, "QC function self.touch is missing");
329 prog->globals.server->self = old_self;
330 prog->globals.server->other = old_other;
338 Slide off of the impacting object
339 returns the blocked flags (1 = floor, 2 = step / wall)
342 #define STOP_EPSILON 0.1
343 void ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
348 backoff = -DotProduct (in, normal) * overbounce;
349 VectorMA(in, backoff, normal, out);
351 for (i = 0;i < 3;i++)
352 if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
361 The basic solid body movement clip that slides along multiple planes
362 Returns the clipflags if the velocity was modified (hit something solid)
366 If stepnormal is not NULL, the plane normal of any vertical wall hit will be stored
369 // LordHavoc: increased from 5 to 32
370 #define MAX_CLIP_PLANES 32
371 int SV_FlyMove (prvm_edict_t *ent, float time, float *stepnormal)
373 int blocked, bumpcount;
374 int i, j, impact, numplanes;
376 vec3_t dir, end, planes[MAX_CLIP_PLANES], primal_velocity, original_velocity, new_velocity;
381 VectorCopy(ent->fields.server->velocity, original_velocity);
382 VectorCopy(ent->fields.server->velocity, primal_velocity);
385 for (bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++)
387 if (!ent->fields.server->velocity[0] && !ent->fields.server->velocity[1] && !ent->fields.server->velocity[2])
390 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
391 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
393 //if (trace.fraction < 0.002)
398 VectorCopy(ent->fields.server->origin, start);
399 start[2] += 3;//0.03125;
400 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
401 end[2] += 3;//0.03125;
402 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
403 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)))
405 Con_Printf("got further (new %f > old %f)\n", testtrace.fraction, trace.fraction);
411 for (i = 0;i < numplanes;i++)
413 VectorCopy(ent->fields.server->origin, start);
414 VectorMA(ent->fields.server->origin, time_left, ent->fields.server->velocity, end);
415 VectorMA(start, 3, planes[i], start);
416 VectorMA(end, 3, planes[i], end);
417 testtrace = SV_Move(start, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
418 if (trace.fraction < testtrace.fraction)
421 VectorCopy(start, ent->fields.server->origin);
426 // VectorAdd(ent->fields.server->origin, planes[j], start);
432 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);
433 if (trace.fraction < 1)
434 Con_Printf(" : %f %f %f", trace.plane.normal[0], trace.plane.normal[1], trace.plane.normal[2]);
439 if (trace.bmodelstartsolid)
441 // LordHavoc: note: this code is what makes entities stick in place
442 // if embedded in world only (you can walk through other objects if
444 // entity is trapped in another solid
445 VectorClear(ent->fields.server->velocity);
450 // break if it moved the entire distance
451 if (trace.fraction == 1)
453 VectorCopy(trace.endpos, ent->fields.server->origin);
459 Con_Printf ("SV_FlyMove: !trace.ent");
460 trace.ent = prog->edicts;
463 if (((int) ent->fields.server->flags & FL_ONGROUND) && ent->fields.server->groundentity == PRVM_EDICT_TO_PROG(trace.ent))
467 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
471 if (trace.plane.normal[2])
473 if (trace.plane.normal[2] > 0.7)
477 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
478 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
485 // save the trace for player extrafriction
487 VectorCopy(trace.plane.normal, stepnormal);
490 if (trace.fraction >= 0.001)
492 // actually covered some distance
493 VectorCopy(trace.endpos, ent->fields.server->origin);
494 VectorCopy(ent->fields.server->velocity, original_velocity);
498 // run the impact function
501 SV_Impact(ent, &trace);
503 // break if removed by the impact function
504 if (ent->priv.server->free)
508 time_left *= 1 - trace.fraction;
510 // clipped to another plane
511 if (numplanes >= MAX_CLIP_PLANES)
513 // this shouldn't really happen
514 VectorClear(ent->fields.server->velocity);
520 for (i = 0;i < numplanes;i++)
521 if (DotProduct(trace.plane.normal, planes[i]) > 0.99)
525 VectorAdd(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity);
530 VectorCopy(trace.plane.normal, planes[numplanes]);
533 if (sv_newflymove.integer)
534 ClipVelocity(ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1);
537 // modify original_velocity so it parallels all of the clip planes
538 for (i = 0;i < numplanes;i++)
540 ClipVelocity(original_velocity, planes[i], new_velocity, 1);
541 for (j = 0;j < numplanes;j++)
546 if (DotProduct(new_velocity, planes[j]) < 0)
556 // go along this plane
557 VectorCopy(new_velocity, ent->fields.server->velocity);
561 // go along the crease
564 VectorClear(ent->fields.server->velocity);
568 CrossProduct(planes[0], planes[1], dir);
569 // LordHavoc: thanks to taniwha of QuakeForge for pointing out this fix for slowed falling in corners
570 VectorNormalize(dir);
571 d = DotProduct(dir, ent->fields.server->velocity);
572 VectorScale(dir, d, ent->fields.server->velocity);
576 // if current velocity is against the original velocity,
577 // stop dead to avoid tiny occilations in sloping corners
578 if (DotProduct(ent->fields.server->velocity, primal_velocity) <= 0)
580 VectorClear(ent->fields.server->velocity);
585 //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]);
588 if ((blocked & 1) == 0 && bumpcount > 1)
590 // LordHavoc: fix the 'fall to your death in a wedge corner' glitch
591 // flag ONGROUND if there's ground under it
592 trace = SV_Move(ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent);
596 // LordHavoc: this came from QW and allows you to get out of water more easily
597 if (sv_gameplayfix_qwplayerphysics.integer && ((int)ent->fields.server->flags & FL_WATERJUMP))
598 VectorCopy(primal_velocity, ent->fields.server->velocity);
608 void SV_AddGravity (prvm_edict_t *ent)
613 val = PRVM_GETEDICTFIELDVALUE(ent, eval_gravity);
614 if (val!=0 && val->_float)
615 ent_gravity = val->_float;
618 ent->fields.server->velocity[2] -= ent_gravity * sv_gravity.value * sv.frametime;
623 ===============================================================================
627 ===============================================================================
634 Does not change the entities velocity at all
637 static trace_t SV_PushEntity (prvm_edict_t *ent, vec3_t push, qboolean failonbmodelstartsolid)
643 VectorAdd (ent->fields.server->origin, push, end);
645 if (ent->fields.server->movetype == MOVETYPE_FLYMISSILE)
647 else if (ent->fields.server->solid == SOLID_TRIGGER || ent->fields.server->solid == SOLID_NOT)
648 type = MOVE_NOMONSTERS; // only clip against bmodels
652 trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, type, ent);
653 if (trace.bmodelstartsolid && failonbmodelstartsolid)
656 VectorCopy (trace.endpos, ent->fields.server->origin);
657 SV_LinkEdict (ent, true);
659 if (ent->fields.server->solid >= SOLID_TRIGGER && trace.ent && (!((int)ent->fields.server->flags & FL_ONGROUND) || ent->fields.server->groundentity != PRVM_EDICT_TO_PROG(trace.ent)))
660 SV_Impact (ent, &trace);
671 void SV_PushMove (prvm_edict_t *pusher, float movetime)
674 float savesolid, movetime2, pushltime;
675 vec3_t mins, maxs, move, move1, moveangle, pushorig, pushang, a, forward, left, up, org;
677 int numcheckentities;
678 static prvm_edict_t *checkentities[MAX_EDICTS];
679 model_t *pushermodel;
682 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])
684 pusher->fields.server->ltime += movetime;
688 switch ((int) pusher->fields.server->solid)
690 // LordHavoc: valid pusher types
694 case SOLID_CORPSE: // LordHavoc: this would be weird...
696 // LordHavoc: no collisions
699 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
700 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
701 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
702 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
703 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
704 pusher->fields.server->ltime += movetime;
705 SV_LinkEdict (pusher, false);
708 Con_Printf("SV_PushMove: entity #%i, unrecognized solid type %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->solid);
711 index = (int) pusher->fields.server->modelindex;
712 if (index < 1 || index >= MAX_MODELS)
714 Con_Printf("SV_PushMove: entity #%i has an invalid modelindex %f\n", PRVM_NUM_FOR_EDICT(pusher), pusher->fields.server->modelindex);
717 pushermodel = sv.models[index];
719 movetime2 = movetime;
720 VectorScale(pusher->fields.server->velocity, movetime2, move1);
721 VectorScale(pusher->fields.server->avelocity, movetime2, moveangle);
722 if (moveangle[0] || moveangle[2])
724 for (i = 0;i < 3;i++)
728 mins[i] = pushermodel->rotatedmins[i] + pusher->fields.server->origin[i] - 1;
729 maxs[i] = pushermodel->rotatedmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
733 mins[i] = pushermodel->rotatedmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
734 maxs[i] = pushermodel->rotatedmaxs[i] + pusher->fields.server->origin[i] + 1;
738 else if (moveangle[1])
740 for (i = 0;i < 3;i++)
744 mins[i] = pushermodel->yawmins[i] + pusher->fields.server->origin[i] - 1;
745 maxs[i] = pushermodel->yawmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
749 mins[i] = pushermodel->yawmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
750 maxs[i] = pushermodel->yawmaxs[i] + pusher->fields.server->origin[i] + 1;
756 for (i = 0;i < 3;i++)
760 mins[i] = pushermodel->normalmins[i] + pusher->fields.server->origin[i] - 1;
761 maxs[i] = pushermodel->normalmaxs[i] + move1[i] + pusher->fields.server->origin[i] + 1;
765 mins[i] = pushermodel->normalmins[i] + move1[i] + pusher->fields.server->origin[i] - 1;
766 maxs[i] = pushermodel->normalmaxs[i] + pusher->fields.server->origin[i] + 1;
771 VectorNegate (moveangle, a);
772 AngleVectorsFLU (a, forward, left, up);
774 VectorCopy (pusher->fields.server->origin, pushorig);
775 VectorCopy (pusher->fields.server->angles, pushang);
776 pushltime = pusher->fields.server->ltime;
778 // move the pusher to its final position
780 VectorMA (pusher->fields.server->origin, movetime, pusher->fields.server->velocity, pusher->fields.server->origin);
781 VectorMA (pusher->fields.server->angles, movetime, pusher->fields.server->avelocity, pusher->fields.server->angles);
782 pusher->fields.server->ltime += movetime;
783 SV_LinkEdict (pusher, false);
785 savesolid = pusher->fields.server->solid;
787 // see if any solid entities are inside the final position
790 numcheckentities = SV_EntitiesInBox(mins, maxs, MAX_EDICTS, checkentities);
791 for (e = 0;e < numcheckentities;e++)
793 prvm_edict_t *check = checkentities[e];
794 if (check->fields.server->movetype == MOVETYPE_NONE
795 || check->fields.server->movetype == MOVETYPE_PUSH
796 || check->fields.server->movetype == MOVETYPE_FOLLOW
797 || check->fields.server->movetype == MOVETYPE_NOCLIP
798 || check->fields.server->movetype == MOVETYPE_FAKEPUSH)
801 // if the entity is standing on the pusher, it will definitely be moved
802 if (((int)check->fields.server->flags & FL_ONGROUND) && PRVM_PROG_TO_EDICT(check->fields.server->groundentity) == pusher)
804 // remove the onground flag for non-players
805 if (check->fields.server->movetype != MOVETYPE_WALK)
806 check->fields.server->flags = (int)check->fields.server->flags & ~FL_ONGROUND;
810 // if the entity is not inside the pusher's final position, leave it alone
811 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)
816 if (forward[0] != 1 || left[1] != 1) // quick way to check if any rotation is used
819 VectorSubtract (check->fields.server->origin, pusher->fields.server->origin, org);
820 org2[0] = DotProduct (org, forward);
821 org2[1] = DotProduct (org, left);
822 org2[2] = DotProduct (org, up);
823 VectorSubtract (org2, org, move);
824 VectorAdd (move, move1, move);
827 VectorCopy (move1, move);
829 VectorCopy (check->fields.server->origin, check->priv.server->moved_from);
830 VectorCopy (check->fields.server->angles, check->priv.server->moved_fromangles);
831 sv.moved_edicts[num_moved++] = check;
833 // try moving the contacted entity
834 pusher->fields.server->solid = SOLID_NOT;
835 trace = SV_PushEntity (check, move, true);
836 // FIXME: turn players specially
837 check->fields.server->angles[1] += trace.fraction * moveangle[1];
838 pusher->fields.server->solid = savesolid; // was SOLID_BSP
839 //Con_Printf("%s:%d frac %f startsolid %d bmodelstartsolid %d allsolid %d\n", __FILE__, __LINE__, trace.fraction, trace.startsolid, trace.bmodelstartsolid, trace.allsolid);
841 // if it is still inside the pusher, block
842 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)
844 // try moving the contacted entity a tiny bit further to account for precision errors
846 pusher->fields.server->solid = SOLID_NOT;
847 VectorScale(move, 1.1, move2);
848 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
849 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
850 SV_PushEntity (check, move2, true);
851 pusher->fields.server->solid = savesolid;
852 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)
854 // try moving the contacted entity a tiny bit less to account for precision errors
855 pusher->fields.server->solid = SOLID_NOT;
856 VectorScale(move, 0.9, move2);
857 VectorCopy (check->priv.server->moved_from, check->fields.server->origin);
858 VectorCopy (check->priv.server->moved_fromangles, check->fields.server->angles);
859 SV_PushEntity (check, move2, true);
860 pusher->fields.server->solid = savesolid;
861 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)
863 // still inside pusher, so it's really blocked
866 if (check->fields.server->mins[0] == check->fields.server->maxs[0])
868 if (check->fields.server->solid == SOLID_NOT || check->fields.server->solid == SOLID_TRIGGER)
871 check->fields.server->mins[0] = check->fields.server->mins[1] = 0;
872 VectorCopy (check->fields.server->mins, check->fields.server->maxs);
876 VectorCopy (pushorig, pusher->fields.server->origin);
877 VectorCopy (pushang, pusher->fields.server->angles);
878 pusher->fields.server->ltime = pushltime;
879 SV_LinkEdict (pusher, false);
881 // move back any entities we already moved
882 for (i = 0;i < num_moved;i++)
884 prvm_edict_t *ed = sv.moved_edicts[i];
885 VectorCopy (ed->priv.server->moved_from, ed->fields.server->origin);
886 VectorCopy (ed->priv.server->moved_fromangles, ed->fields.server->angles);
887 SV_LinkEdict (ed, false);
890 // if the pusher has a "blocked" function, call it, otherwise just stay in place until the obstacle is gone
891 if (pusher->fields.server->blocked)
893 prog->globals.server->self = PRVM_EDICT_TO_PROG(pusher);
894 prog->globals.server->other = PRVM_EDICT_TO_PROG(check);
895 PRVM_ExecuteProgram (pusher->fields.server->blocked, "QC function self.blocked is missing");
902 pusher->fields.server->angles[0] -= 360.0 * floor(pusher->fields.server->angles[0] * (1.0 / 360.0));
903 pusher->fields.server->angles[1] -= 360.0 * floor(pusher->fields.server->angles[1] * (1.0 / 360.0));
904 pusher->fields.server->angles[2] -= 360.0 * floor(pusher->fields.server->angles[2] * (1.0 / 360.0));
913 void SV_Physics_Pusher (prvm_edict_t *ent)
915 float thinktime, oldltime, movetime;
917 oldltime = ent->fields.server->ltime;
919 thinktime = ent->fields.server->nextthink;
920 if (thinktime < ent->fields.server->ltime + sv.frametime)
922 movetime = thinktime - ent->fields.server->ltime;
927 movetime = sv.frametime;
930 // advances ent->fields.server->ltime if not blocked
931 SV_PushMove (ent, movetime);
933 if (thinktime > oldltime && thinktime <= ent->fields.server->ltime)
935 ent->fields.server->nextthink = 0;
936 prog->globals.server->time = sv.time;
937 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
938 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
939 PRVM_ExecuteProgram (ent->fields.server->think, "QC function self.think is missing");
945 ===============================================================================
949 ===============================================================================
956 This is a big hack to try and fix the rare case of getting stuck in the world
960 void SV_CheckStuck (prvm_edict_t *ent)
965 if (!SV_TestEntityPosition(ent))
967 VectorCopy (ent->fields.server->origin, ent->fields.server->oldorigin);
971 VectorCopy (ent->fields.server->origin, org);
973 for (z=-1 ; z< 18 ; z++)
974 for (i=-1 ; i <= 1 ; i++)
975 for (j=-1 ; j <= 1 ; j++)
977 ent->fields.server->origin[0] = org[0] + i;
978 ent->fields.server->origin[1] = org[1] + j;
979 ent->fields.server->origin[2] = org[2] + z;
980 if (!SV_TestEntityPosition(ent))
982 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);
983 SV_LinkEdict (ent, true);
988 VectorCopy (ent->fields.server->oldorigin, ent->fields.server->origin);
989 if (!SV_TestEntityPosition(ent))
991 Con_DPrintf("Unstuck player entity %i (classname \"%s\") by restoring oldorigin.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
992 SV_LinkEdict (ent, true);
996 VectorCopy (org, ent->fields.server->origin);
997 Con_DPrintf("Stuck player entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1000 static void SV_UnstickEntity (prvm_edict_t *ent)
1005 // if not stuck in a bmodel, just return
1006 if (!SV_TestEntityPosition(ent))
1009 VectorCopy (ent->fields.server->origin, org);
1011 for (z=-1 ; z< 18 ; z += 6)
1012 for (i=-1 ; i <= 1 ; i++)
1013 for (j=-1 ; j <= 1 ; j++)
1015 ent->fields.server->origin[0] = org[0] + i;
1016 ent->fields.server->origin[1] = org[1] + j;
1017 ent->fields.server->origin[2] = org[2] + z;
1018 if (!SV_TestEntityPosition(ent))
1020 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);
1021 SV_LinkEdict (ent, true);
1026 VectorCopy (org, ent->fields.server->origin);
1027 if (developer.integer >= 100)
1028 Con_Printf("Stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(ent->fields.server->classname));
1037 qboolean SV_CheckWater (prvm_edict_t *ent)
1040 int nNativeContents;
1043 point[0] = ent->fields.server->origin[0];
1044 point[1] = ent->fields.server->origin[1];
1045 point[2] = ent->fields.server->origin[2] + ent->fields.server->mins[2] + 1;
1047 // DRESK - Support for Entity Contents Transition Event
1048 // NOTE: Some logic needed to be slightly re-ordered
1049 // to not affect performance and allow for the feature.
1051 // Acquire Super Contents Prior to Resets
1052 cont = SV_PointSuperContents(point);
1053 // Acquire Native Contents Here
1054 nNativeContents = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, cont);
1056 // DRESK - Support for Entity Contents Transition Event
1057 if(ent->fields.server->watertype)
1058 // Entity did NOT Spawn; Check
1059 SV_CheckContentsTransition(ent, nNativeContents);
1062 ent->fields.server->waterlevel = 0;
1063 ent->fields.server->watertype = CONTENTS_EMPTY;
1064 cont = SV_PointSuperContents(point);
1065 if (cont & (SUPERCONTENTS_LIQUIDSMASK))
1067 ent->fields.server->watertype = nNativeContents;
1068 ent->fields.server->waterlevel = 1;
1069 point[2] = ent->fields.server->origin[2] + (ent->fields.server->mins[2] + ent->fields.server->maxs[2])*0.5;
1070 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1072 ent->fields.server->waterlevel = 2;
1073 point[2] = ent->fields.server->origin[2] + ent->fields.server->view_ofs[2];
1074 if (SV_PointSuperContents(point) & (SUPERCONTENTS_LIQUIDSMASK))
1075 ent->fields.server->waterlevel = 3;
1079 return ent->fields.server->waterlevel > 1;
1088 void SV_WallFriction (prvm_edict_t *ent, float *stepnormal)
1091 vec3_t forward, into, side;
1093 AngleVectors (ent->fields.server->v_angle, forward, NULL, NULL);
1094 if ((d = DotProduct (stepnormal, forward) + 0.5) < 0)
1096 // cut the tangential velocity
1097 i = DotProduct (stepnormal, ent->fields.server->velocity);
1098 VectorScale (stepnormal, i, into);
1099 VectorSubtract (ent->fields.server->velocity, into, side);
1100 ent->fields.server->velocity[0] = side[0] * (1 + d);
1101 ent->fields.server->velocity[1] = side[1] * (1 + d);
1107 =====================
1110 Player has come to a dead stop, possibly due to the problem with limited
1111 float precision at some angle joins in the BSP hull.
1113 Try fixing by pushing one pixel in each direction.
1115 This is a hack, but in the interest of good gameplay...
1116 ======================
1118 int SV_TryUnstick (prvm_edict_t *ent, vec3_t oldvel)
1123 VectorCopy (ent->fields.server->origin, oldorg);
1126 for (i=0 ; i<8 ; i++)
1128 // try pushing a little in an axial direction
1131 case 0: dir[0] = 2; dir[1] = 0; break;
1132 case 1: dir[0] = 0; dir[1] = 2; break;
1133 case 2: dir[0] = -2; dir[1] = 0; break;
1134 case 3: dir[0] = 0; dir[1] = -2; break;
1135 case 4: dir[0] = 2; dir[1] = 2; break;
1136 case 5: dir[0] = -2; dir[1] = 2; break;
1137 case 6: dir[0] = 2; dir[1] = -2; break;
1138 case 7: dir[0] = -2; dir[1] = -2; break;
1141 SV_PushEntity (ent, dir, false);
1143 // retry the original move
1144 ent->fields.server->velocity[0] = oldvel[0];
1145 ent->fields.server->velocity[1] = oldvel[1];
1146 ent->fields.server->velocity[2] = 0;
1147 clip = SV_FlyMove (ent, 0.1, NULL);
1149 if (fabs(oldorg[1] - ent->fields.server->origin[1]) > 4
1150 || fabs(oldorg[0] - ent->fields.server->origin[0]) > 4)
1152 Con_DPrint("TryUnstick - success.\n");
1156 // go back to the original pos and try again
1157 VectorCopy (oldorg, ent->fields.server->origin);
1161 VectorClear (ent->fields.server->velocity);
1162 Con_DPrint("TryUnstick - failure.\n");
1168 =====================
1171 Only used by players
1172 ======================
1174 void SV_WalkMove (prvm_edict_t *ent)
1176 int clip, oldonground, originalmove_clip, originalmove_flags, originalmove_groundentity;
1177 vec3_t upmove, downmove, start_origin, start_velocity, stepnormal, originalmove_origin, originalmove_velocity;
1180 // if frametime is 0 (due to client sending the same timestamp twice),
1182 if (sv.frametime <= 0)
1185 SV_CheckVelocity(ent);
1187 // do a regular slide move unless it looks like you ran into a step
1188 oldonground = (int)ent->fields.server->flags & FL_ONGROUND;
1189 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1191 VectorCopy (ent->fields.server->origin, start_origin);
1192 VectorCopy (ent->fields.server->velocity, start_velocity);
1194 clip = SV_FlyMove (ent, sv.frametime, NULL);
1196 SV_CheckVelocity(ent);
1198 VectorCopy(ent->fields.server->origin, originalmove_origin);
1199 VectorCopy(ent->fields.server->velocity, originalmove_velocity);
1200 originalmove_clip = clip;
1201 originalmove_flags = (int)ent->fields.server->flags;
1202 originalmove_groundentity = ent->fields.server->groundentity;
1204 if ((int)ent->fields.server->flags & FL_WATERJUMP)
1207 if (sv_nostep.integer)
1210 // if move didn't block on a step, return
1213 // if move was not trying to move into the step, return
1214 if (fabs(start_velocity[0]) < 0.03125 && fabs(start_velocity[1]) < 0.03125)
1217 if (ent->fields.server->movetype != MOVETYPE_FLY)
1219 // return if gibbed by a trigger
1220 if (ent->fields.server->movetype != MOVETYPE_WALK)
1223 // only step up while jumping if that is enabled
1224 if (!(sv_jumpstep.integer && sv_gameplayfix_stepwhilejumping.integer))
1225 if (!oldonground && ent->fields.server->waterlevel == 0)
1229 // try moving up and forward to go up a step
1230 // back to start pos
1231 VectorCopy (start_origin, ent->fields.server->origin);
1232 VectorCopy (start_velocity, ent->fields.server->velocity);
1235 VectorClear (upmove);
1236 upmove[2] = sv_stepheight.value;
1237 // FIXME: don't link?
1238 SV_PushEntity(ent, upmove, false);
1241 ent->fields.server->velocity[2] = 0;
1242 clip = SV_FlyMove (ent, sv.frametime, stepnormal);
1243 ent->fields.server->velocity[2] += start_velocity[2];
1245 SV_CheckVelocity(ent);
1247 // check for stuckness, possibly due to the limited precision of floats
1248 // in the clipping hulls
1250 && fabs(originalmove_origin[1] - ent->fields.server->origin[1]) < 0.03125
1251 && fabs(originalmove_origin[0] - ent->fields.server->origin[0]) < 0.03125)
1253 //Con_Printf("wall\n");
1254 // stepping up didn't make any progress, revert to original move
1255 VectorCopy(originalmove_origin, ent->fields.server->origin);
1256 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1257 //clip = originalmove_clip;
1258 ent->fields.server->flags = originalmove_flags;
1259 ent->fields.server->groundentity = originalmove_groundentity;
1260 // now try to unstick if needed
1261 //clip = SV_TryUnstick (ent, oldvel);
1265 //Con_Printf("step - ");
1267 // extra friction based on view angle
1268 if (clip & 2 && sv_wallfriction.integer)
1269 SV_WallFriction (ent, stepnormal);
1271 // skip out if stepdown is enabled, moving downward, not in water, and the move started onground and ended offground
1272 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)))
1276 VectorClear (downmove);
1277 downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
1278 // FIXME: don't link?
1279 downtrace = SV_PushEntity (ent, downmove, false);
1281 if (downtrace.fraction < 1 && downtrace.plane.normal[2] > 0.7)
1283 // this has been disabled so that you can't jump when you are stepping
1284 // up while already jumping (also known as the Quake2 stair jump bug)
1286 // LordHavoc: disabled this check so you can walk on monsters/players
1287 //if (ent->fields.server->solid == SOLID_BSP)
1289 //Con_Printf("onground\n");
1290 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1291 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(downtrace.ent);
1297 //Con_Printf("slope\n");
1298 // if the push down didn't end up on good ground, use the move without
1299 // the step up. This happens near wall / slope combinations, and can
1300 // cause the player to hop up higher on a slope too steep to climb
1301 VectorCopy(originalmove_origin, ent->fields.server->origin);
1302 VectorCopy(originalmove_velocity, ent->fields.server->velocity);
1303 //clip = originalmove_clip;
1304 ent->fields.server->flags = originalmove_flags;
1305 ent->fields.server->groundentity = originalmove_groundentity;
1308 SV_CheckVelocity(ent);
1311 //============================================================================
1317 Entities that are "stuck" to another entity
1320 void SV_Physics_Follow (prvm_edict_t *ent)
1322 vec3_t vf, vr, vu, angles, v;
1326 if (!SV_RunThink (ent))
1329 // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects
1330 e = PRVM_PROG_TO_EDICT(ent->fields.server->aiment);
1331 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])
1333 // quick case for no rotation
1334 VectorAdd(e->fields.server->origin, ent->fields.server->view_ofs, ent->fields.server->origin);
1338 angles[0] = -ent->fields.server->punchangle[0];
1339 angles[1] = ent->fields.server->punchangle[1];
1340 angles[2] = ent->fields.server->punchangle[2];
1341 AngleVectors (angles, vf, vr, vu);
1342 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];
1343 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];
1344 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];
1345 angles[0] = -e->fields.server->angles[0];
1346 angles[1] = e->fields.server->angles[1];
1347 angles[2] = e->fields.server->angles[2];
1348 AngleVectors (angles, vf, vr, vu);
1349 ent->fields.server->origin[0] = v[0] * vf[0] + v[1] * vf[1] + v[2] * vf[2] + e->fields.server->origin[0];
1350 ent->fields.server->origin[1] = v[0] * vr[0] + v[1] * vr[1] + v[2] * vr[2] + e->fields.server->origin[1];
1351 ent->fields.server->origin[2] = v[0] * vu[0] + v[1] * vu[1] + v[2] * vu[2] + e->fields.server->origin[2];
1353 VectorAdd (e->fields.server->angles, ent->fields.server->v_angle, ent->fields.server->angles);
1354 SV_LinkEdict (ent, true);
1358 ==============================================================================
1362 ==============================================================================
1367 SV_CheckWaterTransition
1371 void SV_CheckWaterTransition (prvm_edict_t *ent)
1374 cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(ent->fields.server->origin));
1375 if (!ent->fields.server->watertype)
1377 // just spawned here
1378 ent->fields.server->watertype = cont;
1379 ent->fields.server->waterlevel = 1;
1383 // DRESK - Support for Entity Contents Transition Event
1384 // NOTE: Call here BEFORE updating the watertype below,
1385 // and suppress watersplash sound if a valid function
1386 // call was made to allow for custom "splash" sounds.
1387 if( !SV_CheckContentsTransition(ent, cont) )
1388 { // Contents Transition Function Invalid; Potentially Play Water Sound
1389 // check if the entity crossed into or out of water
1390 if (sv_sound_watersplash.string && ((ent->fields.server->watertype == CONTENTS_WATER || ent->fields.server->watertype == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
1391 SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1);
1394 if (cont <= CONTENTS_WATER)
1396 ent->fields.server->watertype = cont;
1397 ent->fields.server->waterlevel = 1;
1401 ent->fields.server->watertype = CONTENTS_EMPTY;
1402 ent->fields.server->waterlevel = 0;
1410 Toss, bounce, and fly movement. When onground, do nothing.
1413 void SV_Physics_Toss (prvm_edict_t *ent)
1418 // if onground, return without moving
1419 if ((int)ent->fields.server->flags & FL_ONGROUND)
1421 if (ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer)
1423 // don't stick to ground if onground and moving upward
1424 ent->fields.server->flags -= FL_ONGROUND;
1426 else if (!ent->fields.server->groundentity || !sv_gameplayfix_noairborncorpse.integer)
1428 // we can trust FL_ONGROUND if groundentity is world because it never moves
1431 else if (ent->priv.server->suspendedinairflag && PRVM_PROG_TO_EDICT(ent->fields.server->groundentity)->priv.server->free)
1433 // if ent was supported by a brush model on previous frame,
1434 // and groundentity is now freed, set groundentity to 0 (world)
1435 // which leaves it suspended in the air
1436 ent->fields.server->groundentity = 0;
1440 ent->priv.server->suspendedinairflag = false;
1442 SV_CheckVelocity (ent);
1445 if (ent->fields.server->movetype == MOVETYPE_TOSS || ent->fields.server->movetype == MOVETYPE_BOUNCE)
1446 SV_AddGravity (ent);
1449 VectorMA (ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1452 VectorScale (ent->fields.server->velocity, sv.frametime, move);
1453 trace = SV_PushEntity (ent, move, true);
1454 if (ent->priv.server->free)
1456 if (trace.bmodelstartsolid)
1458 // try to unstick the entity
1459 SV_UnstickEntity(ent);
1460 trace = SV_PushEntity (ent, move, false);
1461 if (ent->priv.server->free)
1465 if (trace.fraction < 1)
1467 if (ent->fields.server->movetype == MOVETYPE_BOUNCEMISSILE)
1469 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 2.0);
1470 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1472 else if (ent->fields.server->movetype == MOVETYPE_BOUNCE)
1475 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.5);
1476 // LordHavoc: fixed grenades not bouncing when fired down a slope
1477 if (sv_gameplayfix_grenadebouncedownslopes.integer)
1479 d = DotProduct(trace.plane.normal, ent->fields.server->velocity);
1480 if (trace.plane.normal[2] > 0.7 && fabs(d) < 60)
1482 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1483 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1484 VectorClear (ent->fields.server->velocity);
1485 VectorClear (ent->fields.server->avelocity);
1488 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1492 if (trace.plane.normal[2] > 0.7 && ent->fields.server->velocity[2] < 60)
1494 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1495 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1496 VectorClear (ent->fields.server->velocity);
1497 VectorClear (ent->fields.server->avelocity);
1500 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1505 ClipVelocity (ent->fields.server->velocity, trace.plane.normal, ent->fields.server->velocity, 1.0);
1506 if (trace.plane.normal[2] > 0.7)
1508 ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND;
1509 ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent);
1510 if (((prvm_edict_t *)trace.ent)->fields.server->solid == SOLID_BSP)
1511 ent->priv.server->suspendedinairflag = true;
1512 VectorClear (ent->fields.server->velocity);
1513 VectorClear (ent->fields.server->avelocity);
1516 ent->fields.server->flags = (int)ent->fields.server->flags & ~FL_ONGROUND;
1520 // check for in water
1521 SV_CheckWaterTransition (ent);
1525 ===============================================================================
1529 ===============================================================================
1536 Monsters freefall when they don't have a ground entity, otherwise
1537 all movement is done with discrete steps.
1539 This is also used for objects that have become still on the ground, but
1540 will fall if the floor is pulled out from under them.
1543 void SV_Physics_Step (prvm_edict_t *ent)
1545 int flags = (int)ent->fields.server->flags;
1546 // don't fall at all if fly/swim
1547 if (!(flags & (FL_FLY | FL_SWIM)))
1549 if (flags & FL_ONGROUND)
1551 // freefall if onground and moving upward
1552 // freefall if not standing on a world surface (it may be a lift or trap door)
1553 if ((ent->fields.server->velocity[2] >= (1.0 / 32.0) && sv_gameplayfix_upwardvelocityclearsongroundflag.integer) || ent->fields.server->groundentity)
1555 ent->fields.server->flags -= FL_ONGROUND;
1557 SV_CheckVelocity(ent);
1558 SV_FlyMove(ent, sv.frametime, NULL);
1559 SV_LinkEdict(ent, true);
1564 // freefall if not onground
1565 int hitsound = ent->fields.server->velocity[2] < sv_gravity.value * -0.1;
1568 SV_CheckVelocity(ent);
1569 SV_FlyMove(ent, sv.frametime, NULL);
1570 SV_LinkEdict(ent, true);
1573 if (hitsound && (int)ent->fields.server->flags & FL_ONGROUND && sv_sound_land.string)
1574 SV_StartSound(ent, 0, sv_sound_land.string, 255, 1);
1581 SV_CheckWaterTransition(ent);
1584 //============================================================================
1586 static void SV_Physics_Entity (prvm_edict_t *ent)
1588 // don't run a move on newly spawned projectiles as it messes up movement
1589 // interpolation and rocket trails
1590 qboolean runmove = ent->priv.server->move;
1591 ent->priv.server->move = true;
1592 switch ((int) ent->fields.server->movetype)
1595 case MOVETYPE_FAKEPUSH:
1596 SV_Physics_Pusher (ent);
1599 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1600 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1603 case MOVETYPE_FOLLOW:
1604 SV_Physics_Follow (ent);
1606 case MOVETYPE_NOCLIP:
1607 if (SV_RunThink(ent))
1610 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1611 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1613 SV_LinkEdict(ent, false);
1616 SV_Physics_Step (ent);
1619 if (SV_RunThink (ent))
1621 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1622 SV_AddGravity (ent);
1623 SV_CheckStuck (ent);
1625 SV_LinkEdict (ent, true);
1629 case MOVETYPE_BOUNCE:
1630 case MOVETYPE_BOUNCEMISSILE:
1631 case MOVETYPE_FLYMISSILE:
1634 if (SV_RunThink (ent) && runmove)
1635 SV_Physics_Toss (ent);
1638 Con_Printf ("SV_Physics: bad movetype %i\n", (int)ent->fields.server->movetype);
1643 void SV_Physics_ClientEntity (prvm_edict_t *ent)
1645 SV_ApplyClientMove();
1646 // make sure the velocity is sane (not a NaN)
1647 SV_CheckVelocity(ent);
1648 // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
1649 if (SV_PlayerPhysicsQC && sv_playerphysicsqc.integer)
1651 prog->globals.server->time = sv.time;
1652 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1653 PRVM_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - prog->functions), "QC function SV_PlayerPhysics is missing");
1657 // make sure the velocity is sane (not a NaN)
1658 SV_CheckVelocity(ent);
1659 // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
1660 // player_run/player_stand1 does not horribly malfunction if the
1661 // velocity becomes a number that is both == 0 and != 0
1662 // (sounds to me like NaN but to be absolutely safe...)
1663 if (DotProduct(ent->fields.server->velocity, ent->fields.server->velocity) < 0.0001)
1664 VectorClear(ent->fields.server->velocity);
1665 // call standard client pre-think
1666 prog->globals.server->time = sv.time;
1667 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1668 PRVM_ExecuteProgram (prog->globals.server->PlayerPreThink, "QC function PlayerPreThink is missing");
1669 SV_CheckVelocity (ent);
1671 switch ((int) ent->fields.server->movetype)
1674 case MOVETYPE_FAKEPUSH:
1675 SV_Physics_Pusher (ent);
1678 // LordHavoc: manually inlined the thinktime check here because MOVETYPE_NONE is used on so many objects
1679 if (ent->fields.server->nextthink > 0 && ent->fields.server->nextthink <= sv.time + sv.frametime)
1682 case MOVETYPE_FOLLOW:
1683 SV_Physics_Follow (ent);
1685 case MOVETYPE_NOCLIP:
1686 if (SV_RunThink(ent))
1689 VectorMA(ent->fields.server->origin, sv.frametime, ent->fields.server->velocity, ent->fields.server->origin);
1690 VectorMA(ent->fields.server->angles, sv.frametime, ent->fields.server->avelocity, ent->fields.server->angles);
1694 SV_Physics_Step (ent);
1697 if (SV_RunThink (ent))
1699 if (!SV_CheckWater (ent) && ! ((int)ent->fields.server->flags & FL_WATERJUMP) )
1700 SV_AddGravity (ent);
1701 SV_CheckStuck (ent);
1706 case MOVETYPE_BOUNCE:
1707 case MOVETYPE_BOUNCEMISSILE:
1708 case MOVETYPE_FLYMISSILE:
1710 if (SV_RunThink (ent))
1711 SV_Physics_Toss (ent);
1714 if (SV_RunThink (ent))
1716 SV_CheckWater (ent);
1721 Con_Printf ("SV_Physics_ClientEntity: bad movetype %i\n", (int)ent->fields.server->movetype);
1725 SV_CheckVelocity (ent);
1727 // call standard player post-think
1728 SV_LinkEdict (ent, true);
1730 SV_CheckVelocity (ent);
1732 prog->globals.server->time = sv.time;
1733 prog->globals.server->self = PRVM_EDICT_TO_PROG(ent);
1734 PRVM_ExecuteProgram (prog->globals.server->PlayerPostThink, "QC function PlayerPostThink is missing");
1743 void SV_Physics (void)
1748 // let the progs know that a new frame has started
1749 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1750 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1751 prog->globals.server->time = sv.time;
1752 prog->globals.server->frametime = sv.frametime;
1753 PRVM_ExecuteProgram (prog->globals.server->StartFrame, "QC function StartFrame is missing");
1756 // treat each object in turn
1759 // if force_retouch, relink all the entities
1760 if (prog->globals.server->force_retouch > 0)
1761 for (i = 1, ent = PRVM_EDICT_NUM(i);i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1762 if (!ent->priv.server->free)
1763 SV_LinkEdict (ent, true); // force retouch even for stationary
1765 // run physics on the client entities
1766 for (i = 1, ent = PRVM_EDICT_NUM(i), host_client = svs.clients;i <= svs.maxclients;i++, ent = PRVM_NEXT_EDICT(ent), host_client++)
1768 if (!ent->priv.server->free)
1770 // don't do physics on disconnected clients, FrikBot relies on this
1771 if (!host_client->spawned)
1772 memset(&host_client->cmd, 0, sizeof(host_client->cmd));
1773 // don't run physics here if running asynchronously
1774 else if (host_client->clmovement_skipphysicsframes > 0)
1775 host_client->clmovement_skipphysicsframes--;
1777 SV_Physics_ClientEntity(ent);
1781 // run physics on all the non-client entities
1782 if (!sv_freezenonclients.integer)
1783 for (;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent))
1784 if (!ent->priv.server->free)
1785 SV_Physics_Entity(ent);
1787 if (prog->globals.server->force_retouch > 0)
1788 prog->globals.server->force_retouch = max(0, prog->globals.server->force_retouch - 1);
1790 // LordHavoc: endframe support
1793 prog->globals.server->self = PRVM_EDICT_TO_PROG(prog->edicts);
1794 prog->globals.server->other = PRVM_EDICT_TO_PROG(prog->edicts);
1795 prog->globals.server->time = sv.time;
1796 PRVM_ExecuteProgram ((func_t)(EndFrameQC - prog->functions), "QC function EndFrame is missing");
1799 // decrement prog->num_edicts if the highest number entities died
1800 for (;PRVM_EDICT_NUM(prog->num_edicts - 1)->priv.server->free;prog->num_edicts--);
1802 if (!sv_freezenonclients.integer)
1803 sv.time += sv.frametime;
1807 trace_t SV_Trace_Toss (prvm_edict_t *tossent, prvm_edict_t *ignore)
1812 vec3_t original_origin;
1813 vec3_t original_velocity;
1814 vec3_t original_angles;
1815 vec3_t original_avelocity;
1819 VectorCopy(tossent->fields.server->origin , original_origin );
1820 VectorCopy(tossent->fields.server->velocity , original_velocity );
1821 VectorCopy(tossent->fields.server->angles , original_angles );
1822 VectorCopy(tossent->fields.server->avelocity, original_avelocity);
1824 val = PRVM_GETEDICTFIELDVALUE(tossent, eval_gravity);
1825 if (val != NULL && val->_float != 0)
1826 gravity = val->_float;
1829 gravity *= sv_gravity.value * 0.05;
1831 for (i = 0;i < 200;i++) // LordHavoc: sanity check; never trace more than 10 seconds
1833 SV_CheckVelocity (tossent);
1834 tossent->fields.server->velocity[2] -= gravity;
1835 VectorMA (tossent->fields.server->angles, 0.05, tossent->fields.server->avelocity, tossent->fields.server->angles);
1836 VectorScale (tossent->fields.server->velocity, 0.05, move);
1837 VectorAdd (tossent->fields.server->origin, move, end);
1838 trace = SV_Move (tossent->fields.server->origin, tossent->fields.server->mins, tossent->fields.server->maxs, end, MOVE_NORMAL, tossent);
1839 VectorCopy (trace.endpos, tossent->fields.server->origin);
1841 if (trace.fraction < 1)
1845 VectorCopy(original_origin , tossent->fields.server->origin );
1846 VectorCopy(original_velocity , tossent->fields.server->velocity );
1847 VectorCopy(original_angles , tossent->fields.server->angles );
1848 VectorCopy(original_avelocity, tossent->fields.server->avelocity);