1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20// sv_phys.c
21
22#include "qwsvdef.h"
23
24/*
25
26
27pushmove 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.
28
29onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
30
31doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
32bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
33corpses are SOLID_NOT and MOVETYPE_TOSS
34crates are SOLID_BBOX and MOVETYPE_TOSS
35walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
36flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
37
38solid_edge items only clip against bsp models.
39
40*/
41
42cvar_t	sv_maxvelocity = {"sv_maxvelocity","2000"};
43
44cvar_t	sv_gravity			 = { "sv_gravity", "800"};
45cvar_t	sv_stopspeed		 = { "sv_stopspeed", "100"};
46cvar_t	sv_maxspeed			 = { "sv_maxspeed", "320"};
47cvar_t	sv_spectatormaxspeed = { "sv_spectatormaxspeed", "500"};
48cvar_t	sv_accelerate		 = { "sv_accelerate", "10"};
49cvar_t	sv_airaccelerate	 = { "sv_airaccelerate", "0.7"};
50cvar_t	sv_wateraccelerate	 = { "sv_wateraccelerate", "10"};
51cvar_t	sv_friction			 = { "sv_friction", "4"};
52cvar_t	sv_waterfriction	 = { "sv_waterfriction", "4"};
53
54
55#define	MOVE_EPSILON	0.01
56
57void SV_Physics_Toss (edict_t *ent);
58
59/*
60================
61SV_CheckAllEnts
62================
63*/
64void SV_CheckAllEnts (void)
65{
66	int			e;
67	edict_t		*check;
68
69// see if any solid entities are inside the final position
70	check = NEXT_EDICT(sv.edicts);
71	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
72	{
73		if (check->free)
74			continue;
75		if (check->v.movetype == MOVETYPE_PUSH
76		|| check->v.movetype == MOVETYPE_NONE
77		|| check->v.movetype == MOVETYPE_NOCLIP)
78			continue;
79
80		if (SV_TestEntityPosition (check))
81			Con_Printf ("entity in invalid position\n");
82	}
83}
84
85/*
86================
87SV_CheckVelocity
88================
89*/
90void SV_CheckVelocity (edict_t *ent)
91{
92	int		i;
93
94//
95// bound velocity
96//
97	for (i=0 ; i<3 ; i++)
98	{
99		if (IS_NAN(ent->v.velocity[i]))
100		{
101			Con_Printf ("Got a NaN velocity on %s\n", PR_GetString(ent->v.classname));
102			ent->v.velocity[i] = 0;
103		}
104		if (IS_NAN(ent->v.origin[i]))
105		{
106			Con_Printf ("Got a NaN origin on %s\n", PR_GetString(ent->v.classname));
107			ent->v.origin[i] = 0;
108		}
109		if (ent->v.velocity[i] > sv_maxvelocity.value)
110			ent->v.velocity[i] = sv_maxvelocity.value;
111		else if (ent->v.velocity[i] < -sv_maxvelocity.value)
112			ent->v.velocity[i] = -sv_maxvelocity.value;
113	}
114}
115
116/*
117=============
118SV_RunThink
119
120Runs thinking code if time.  There is some play in the exact time the think
121function will be called, because it is called before any movement is done
122in a frame.  Not used for pushmove objects, because they must be exact.
123Returns false if the entity removed itself.
124=============
125*/
126qboolean SV_RunThink (edict_t *ent)
127{
128	float	thinktime;
129
130	do
131	{
132		thinktime = ent->v.nextthink;
133		if (thinktime <= 0)
134			return true;
135		if (thinktime > sv.time + host_frametime)
136			return true;
137
138		if (thinktime < sv.time)
139			thinktime = sv.time;	// don't let things stay in the past.
140									// it is possible to start that way
141									// by a trigger with a local time.
142		ent->v.nextthink = 0;
143		pr_global_struct->time = thinktime;
144		pr_global_struct->self = EDICT_TO_PROG(ent);
145		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
146		PR_ExecuteProgram (ent->v.think);
147
148		if (ent->free)
149			return false;
150	} while (1);
151
152	return true;
153}
154
155/*
156==================
157SV_Impact
158
159Two entities have touched, so run their touch functions
160==================
161*/
162void SV_Impact (edict_t *e1, edict_t *e2)
163{
164	int		old_self, old_other;
165
166	old_self = pr_global_struct->self;
167	old_other = pr_global_struct->other;
168
169	pr_global_struct->time = sv.time;
170	if (e1->v.touch && e1->v.solid != SOLID_NOT)
171	{
172		pr_global_struct->self = EDICT_TO_PROG(e1);
173		pr_global_struct->other = EDICT_TO_PROG(e2);
174		PR_ExecuteProgram (e1->v.touch);
175	}
176
177	if (e2->v.touch && e2->v.solid != SOLID_NOT)
178	{
179		pr_global_struct->self = EDICT_TO_PROG(e2);
180		pr_global_struct->other = EDICT_TO_PROG(e1);
181		PR_ExecuteProgram (e2->v.touch);
182	}
183
184	pr_global_struct->self = old_self;
185	pr_global_struct->other = old_other;
186}
187
188
189/*
190==================
191ClipVelocity
192
193Slide off of the impacting object
194returns the blocked flags (1 = floor, 2 = step / wall)
195==================
196*/
197#define	STOP_EPSILON	0.1
198
199int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
200{
201	float	backoff;
202	float	change;
203	int		i, blocked;
204
205	blocked = 0;
206	if (normal[2] > 0)
207		blocked |= 1;		// floor
208	if (!normal[2])
209		blocked |= 2;		// step
210
211	backoff = DotProduct (in, normal) * overbounce;
212
213	for (i=0 ; i<3 ; i++)
214	{
215		change = normal[i]*backoff;
216		out[i] = in[i] - change;
217		if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
218			out[i] = 0;
219	}
220
221	return blocked;
222}
223
224
225/*
226============
227SV_FlyMove
228
229The basic solid body movement clip that slides along multiple planes
230Returns the clipflags if the velocity was modified (hit something solid)
2311 = floor
2322 = wall / step
2334 = dead stop
234If steptrace is not NULL, the trace of any vertical wall hit will be stored
235============
236*/
237#define	MAX_CLIP_PLANES	5
238int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace)
239{
240	int			bumpcount, numbumps;
241	vec3_t		dir;
242	float		d;
243	int			numplanes;
244	vec3_t		planes[MAX_CLIP_PLANES];
245	vec3_t		primal_velocity, original_velocity, new_velocity;
246	int			i, j;
247	trace_t		trace;
248	vec3_t		end;
249	float		time_left;
250	int			blocked;
251
252	numbumps = 4;
253
254	blocked = 0;
255	VectorCopy (ent->v.velocity, original_velocity);
256	VectorCopy (ent->v.velocity, primal_velocity);
257	numplanes = 0;
258
259	time_left = time;
260
261	for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
262	{
263		for (i=0 ; i<3 ; i++)
264			end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i];
265
266		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, false, ent);
267
268		if (trace.allsolid)
269		{	// entity is trapped in another solid
270			VectorCopy (vec3_origin, ent->v.velocity);
271			return 3;
272		}
273
274		if (trace.fraction > 0)
275		{	// actually covered some distance
276			VectorCopy (trace.endpos, ent->v.origin);
277			VectorCopy (ent->v.velocity, original_velocity);
278			numplanes = 0;
279		}
280
281		if (trace.fraction == 1)
282			 break;		// moved the entire distance
283
284		if (!trace.ent)
285			SV_Error ("SV_FlyMove: !trace.ent");
286
287		if (trace.plane.normal[2] > 0.7)
288		{
289			blocked |= 1;		// floor
290			if (trace.ent->v.solid == SOLID_BSP)
291			{
292				ent->v.flags =	(int)ent->v.flags | FL_ONGROUND;
293				ent->v.groundentity = EDICT_TO_PROG(trace.ent);
294			}
295		}
296		if (!trace.plane.normal[2])
297		{
298			blocked |= 2;		// step
299			if (steptrace)
300				*steptrace = trace;	// save for player extrafriction
301		}
302
303//
304// run the impact function
305//
306		SV_Impact (ent, trace.ent);
307		if (ent->free)
308			break;		// removed by the impact function
309
310
311		time_left -= time_left * trace.fraction;
312
313	// cliped to another plane
314		if (numplanes >= MAX_CLIP_PLANES)
315		{	// this shouldn't really happen
316			VectorCopy (vec3_origin, ent->v.velocity);
317			return 3;
318		}
319
320		VectorCopy (trace.plane.normal, planes[numplanes]);
321		numplanes++;
322
323//
324// modify original_velocity so it parallels all of the clip planes
325//
326		for (i=0 ; i<numplanes ; i++)
327		{
328			ClipVelocity (original_velocity, planes[i], new_velocity, 1);
329			for (j=0 ; j<numplanes ; j++)
330				if (j != i)
331				{
332					if (DotProduct (new_velocity, planes[j]) < 0)
333						break;	// not ok
334				}
335			if (j == numplanes)
336				break;
337		}
338
339		if (i != numplanes)
340		{	// go along this plane
341			VectorCopy (new_velocity, ent->v.velocity);
342		}
343		else
344		{	// go along the crease
345			if (numplanes != 2)
346			{
347//				Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
348				VectorCopy (vec3_origin, ent->v.velocity);
349				return 7;
350			}
351			CrossProduct (planes[0], planes[1], dir);
352			d = DotProduct (dir, ent->v.velocity);
353			VectorScale (dir, d, ent->v.velocity);
354		}
355
356//
357// if original velocity is against the original velocity, stop dead
358// to avoid tiny occilations in sloping corners
359//
360		if (DotProduct (ent->v.velocity, primal_velocity) <= 0)
361		{
362			VectorCopy (vec3_origin, ent->v.velocity);
363			return blocked;
364		}
365	}
366
367	return blocked;
368}
369
370
371/*
372============
373SV_AddGravity
374
375============
376*/
377void SV_AddGravity (edict_t *ent, float scale)
378{
379	ent->v.velocity[2] -= scale * movevars.gravity * host_frametime;
380}
381
382/*
383===============================================================================
384
385PUSHMOVE
386
387===============================================================================
388*/
389
390/*
391============
392SV_PushEntity
393
394Does not change the entities velocity at all
395============
396*/
397trace_t SV_PushEntity (edict_t *ent, vec3_t push)
398{
399	trace_t	trace;
400	vec3_t	end;
401
402	VectorAdd (ent->v.origin, push, end);
403
404	if (ent->v.movetype == MOVETYPE_FLYMISSILE)
405		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent);
406	else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT)
407	// only clip against bmodels
408		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent);
409	else
410		trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent);
411
412	VectorCopy (trace.endpos, ent->v.origin);
413	SV_LinkEdict (ent, true);
414
415	if (trace.ent)
416		SV_Impact (ent, trace.ent);
417
418	return trace;
419}
420
421
422/*
423============
424SV_Push
425
426============
427*/
428qboolean SV_Push (edict_t *pusher, vec3_t move)
429{
430	int			i, e;
431	edict_t		*check, *block;
432	vec3_t		mins, maxs;
433	vec3_t		pushorig;
434	int			num_moved;
435	edict_t		*moved_edict[MAX_EDICTS];
436	vec3_t		moved_from[MAX_EDICTS];
437
438	for (i=0 ; i<3 ; i++)
439	{
440		mins[i] = pusher->v.absmin[i] + move[i];
441		maxs[i] = pusher->v.absmax[i] + move[i];
442	}
443
444	VectorCopy (pusher->v.origin, pushorig);
445
446// move the pusher to it's final position
447
448	VectorAdd (pusher->v.origin, move, pusher->v.origin);
449	SV_LinkEdict (pusher, false);
450
451// see if any solid entities are inside the final position
452	num_moved = 0;
453	check = NEXT_EDICT(sv.edicts);
454	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
455	{
456		if (check->free)
457			continue;
458		if (check->v.movetype == MOVETYPE_PUSH
459		|| check->v.movetype == MOVETYPE_NONE
460		|| check->v.movetype == MOVETYPE_NOCLIP)
461			continue;
462
463		pusher->v.solid = SOLID_NOT;
464		block = SV_TestEntityPosition (check);
465		pusher->v.solid = SOLID_BSP;
466		if (block)
467			continue;
468
469	// if the entity is standing on the pusher, it will definately be moved
470		if ( ! ( ((int)check->v.flags & FL_ONGROUND)
471		&& PROG_TO_EDICT(check->v.groundentity) == pusher) )
472		{
473			if ( check->v.absmin[0] >= maxs[0]
474			|| check->v.absmin[1] >= maxs[1]
475			|| check->v.absmin[2] >= maxs[2]
476			|| check->v.absmax[0] <= mins[0]
477			|| check->v.absmax[1] <= mins[1]
478			|| check->v.absmax[2] <= mins[2] )
479				continue;
480
481		// see if the ent's bbox is inside the pusher's final position
482			if (!SV_TestEntityPosition (check))
483				continue;
484		}
485
486		VectorCopy (check->v.origin, moved_from[num_moved]);
487		moved_edict[num_moved] = check;
488		num_moved++;
489
490		// try moving the contacted entity
491		VectorAdd (check->v.origin, move, check->v.origin);
492		block = SV_TestEntityPosition (check);
493		if (!block)
494		{	// pushed ok
495			SV_LinkEdict (check, false);
496			continue;
497		}
498
499		// if it is ok to leave in the old position, do it
500		VectorSubtract (check->v.origin, move, check->v.origin);
501		block = SV_TestEntityPosition (check);
502		if (!block)
503		{
504			num_moved--;
505			continue;
506		}
507
508	// if it is still inside the pusher, block
509		if (check->v.mins[0] == check->v.maxs[0])
510		{
511			SV_LinkEdict (check, false);
512			continue;
513		}
514		if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
515		{	// corpse
516			check->v.mins[0] = check->v.mins[1] = 0;
517			VectorCopy (check->v.mins, check->v.maxs);
518			SV_LinkEdict (check, false);
519			continue;
520		}
521
522		VectorCopy (pushorig, pusher->v.origin);
523		SV_LinkEdict (pusher, false);
524
525		// if the pusher has a "blocked" function, call it
526		// otherwise, just stay in place until the obstacle is gone
527		if (pusher->v.blocked)
528		{
529			pr_global_struct->self = EDICT_TO_PROG(pusher);
530			pr_global_struct->other = EDICT_TO_PROG(check);
531			PR_ExecuteProgram (pusher->v.blocked);
532		}
533
534	// move back any entities we already moved
535		for (i=0 ; i<num_moved ; i++)
536		{
537			VectorCopy (moved_from[i], moved_edict[i]->v.origin);
538			SV_LinkEdict (moved_edict[i], false);
539		}
540		return false;
541	}
542
543	return true;
544}
545
546/*
547============
548SV_PushMove
549
550============
551*/
552void SV_PushMove (edict_t *pusher, float movetime)
553{
554	int			i;
555	vec3_t		move;
556
557	if (!pusher->v.velocity[0] && !pusher->v.velocity[1] && !pusher->v.velocity[2])
558	{
559		pusher->v.ltime += movetime;
560		return;
561	}
562
563	for (i=0 ; i<3 ; i++)
564		move[i] = pusher->v.velocity[i] * movetime;
565
566	if (SV_Push (pusher, move))
567		pusher->v.ltime += movetime;
568}
569
570
571/*
572================
573SV_Physics_Pusher
574
575================
576*/
577void SV_Physics_Pusher (edict_t *ent)
578{
579	float	thinktime;
580	float	oldltime;
581	float	movetime;
582vec3_t oldorg, move;
583float	l;
584
585	oldltime = ent->v.ltime;
586
587	thinktime = ent->v.nextthink;
588	if (thinktime < ent->v.ltime + host_frametime)
589	{
590		movetime = thinktime - ent->v.ltime;
591		if (movetime < 0)
592			movetime = 0;
593	}
594	else
595		movetime = host_frametime;
596
597	if (movetime)
598	{
599		SV_PushMove (ent, movetime);	// advances ent->v.ltime if not blocked
600	}
601
602	if (thinktime > oldltime && thinktime <= ent->v.ltime)
603	{
604VectorCopy (ent->v.origin, oldorg);
605		ent->v.nextthink = 0;
606		pr_global_struct->time = sv.time;
607		pr_global_struct->self = EDICT_TO_PROG(ent);
608		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
609		PR_ExecuteProgram (ent->v.think);
610		if (ent->free)
611			return;
612VectorSubtract (ent->v.origin, oldorg, move);
613
614l = Length(move);
615if (l > 1.0/64)
616{
617//	Con_Printf ("**** snap: %f\n", Length (l));
618	VectorCopy (oldorg, ent->v.origin);
619	SV_Push (ent, move);
620}
621
622	}
623
624}
625
626
627/*
628=============
629SV_Physics_None
630
631Non moving objects can only think
632=============
633*/
634void SV_Physics_None (edict_t *ent)
635{
636// regular thinking
637	SV_RunThink (ent);
638}
639
640/*
641=============
642SV_Physics_Noclip
643
644A moving object that doesn't obey physics
645=============
646*/
647void SV_Physics_Noclip (edict_t *ent)
648{
649// regular thinking
650	if (!SV_RunThink (ent))
651		return;
652
653	VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
654	VectorMA (ent->v.origin, host_frametime, ent->v.velocity, ent->v.origin);
655
656	SV_LinkEdict (ent, false);
657}
658
659/*
660==============================================================================
661
662TOSS / BOUNCE
663
664==============================================================================
665*/
666
667/*
668=============
669SV_CheckWaterTransition
670
671=============
672*/
673void SV_CheckWaterTransition (edict_t *ent)
674{
675	int		cont;
676
677	cont = SV_PointContents (ent->v.origin);
678	if (!ent->v.watertype)
679	{	// just spawned here
680		ent->v.watertype = cont;
681		ent->v.waterlevel = 1;
682		return;
683	}
684
685	if (cont <= CONTENTS_WATER)
686	{
687		if (ent->v.watertype == CONTENTS_EMPTY)
688		{	// just crossed into water
689			SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
690		}
691		ent->v.watertype = cont;
692		ent->v.waterlevel = 1;
693	}
694	else
695	{
696		if (ent->v.watertype != CONTENTS_EMPTY)
697		{	// just crossed into water
698			SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
699		}
700		ent->v.watertype = CONTENTS_EMPTY;
701		ent->v.waterlevel = cont;
702	}
703}
704
705/*
706=============
707SV_Physics_Toss
708
709Toss, bounce, and fly movement.  When onground, do nothing.
710=============
711*/
712void SV_Physics_Toss (edict_t *ent)
713{
714	trace_t	trace;
715	vec3_t	move;
716	float	backoff;
717
718// regular thinking
719	if (!SV_RunThink (ent))
720		return;
721
722	if (ent->v.velocity[2] > 0)
723		ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND;
724
725// if onground, return without moving
726	if ( ((int)ent->v.flags & FL_ONGROUND) )
727		return;
728
729	SV_CheckVelocity (ent);
730
731// add gravity
732	if (ent->v.movetype != MOVETYPE_FLY
733	&& ent->v.movetype != MOVETYPE_FLYMISSILE)
734		SV_AddGravity (ent, 1.0);
735
736// move angles
737	VectorMA (ent->v.angles, host_frametime, ent->v.avelocity, ent->v.angles);
738
739// move origin
740	VectorScale (ent->v.velocity, host_frametime, move);
741	trace = SV_PushEntity (ent, move);
742	if (trace.fraction == 1)
743		return;
744	if (ent->free)
745		return;
746
747	if (ent->v.movetype == MOVETYPE_BOUNCE)
748		backoff = 1.5;
749	else
750		backoff = 1;
751
752	ClipVelocity (ent->v.velocity, trace.plane.normal, ent->v.velocity, backoff);
753
754// stop if on ground
755	if (trace.plane.normal[2] > 0.7)
756	{
757		if (ent->v.velocity[2] < 60 || ent->v.movetype != MOVETYPE_BOUNCE )
758		{
759			ent->v.flags = (int)ent->v.flags | FL_ONGROUND;
760			ent->v.groundentity = EDICT_TO_PROG(trace.ent);
761			VectorCopy (vec3_origin, ent->v.velocity);
762			VectorCopy (vec3_origin, ent->v.avelocity);
763		}
764	}
765
766// check for in water
767	SV_CheckWaterTransition (ent);
768}
769
770/*
771===============================================================================
772
773STEPPING MOVEMENT
774
775===============================================================================
776*/
777
778/*
779=============
780SV_Physics_Step
781
782Monsters freefall when they don't have a ground entity, otherwise
783all movement is done with discrete steps.
784
785This is also used for objects that have become still on the ground, but
786will fall if the floor is pulled out from under them.
787FIXME: is this true?
788=============
789*/
790void SV_Physics_Step (edict_t *ent)
791{
792	qboolean	hitsound;
793
794// frefall if not onground
795	if ( ! ((int)ent->v.flags & (FL_ONGROUND | FL_FLY | FL_SWIM) ) )
796	{
797		if (ent->v.velocity[2] < movevars.gravity*-0.1)
798			hitsound = true;
799		else
800			hitsound = false;
801
802		SV_AddGravity (ent, 1.0);
803		SV_CheckVelocity (ent);
804		SV_FlyMove (ent, host_frametime, NULL);
805		SV_LinkEdict (ent, true);
806
807		if ( (int)ent->v.flags & FL_ONGROUND )	// just hit ground
808		{
809			if (hitsound)
810				SV_StartSound (ent, 0, "demon/dland2.wav", 255, 1);
811		}
812	}
813
814// regular thinking
815	SV_RunThink (ent);
816
817	SV_CheckWaterTransition (ent);
818}
819
820//============================================================================
821
822void SV_ProgStartFrame (void)
823{
824// let the progs know that a new frame has started
825	pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
826	pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
827	pr_global_struct->time = sv.time;
828	PR_ExecuteProgram (pr_global_struct->StartFrame);
829}
830
831/*
832================
833SV_RunEntity
834
835================
836*/
837void SV_RunEntity (edict_t *ent)
838{
839	if (ent->v.lastruntime == (float)realtime)
840		return;
841	ent->v.lastruntime = (float)realtime;
842
843	switch ( (int)ent->v.movetype)
844	{
845	case MOVETYPE_PUSH:
846		SV_Physics_Pusher (ent);
847		break;
848	case MOVETYPE_NONE:
849		SV_Physics_None (ent);
850		break;
851	case MOVETYPE_NOCLIP:
852		SV_Physics_Noclip (ent);
853		break;
854	case MOVETYPE_STEP:
855		SV_Physics_Step (ent);
856		break;
857	case MOVETYPE_TOSS:
858	case MOVETYPE_BOUNCE:
859	case MOVETYPE_FLY:
860	case MOVETYPE_FLYMISSILE:
861		SV_Physics_Toss (ent);
862		break;
863	default:
864		SV_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);
865	}
866}
867
868/*
869================
870SV_RunNewmis
871
872================
873*/
874void SV_RunNewmis (void)
875{
876	edict_t	*ent;
877
878	if (!pr_global_struct->newmis)
879		return;
880	ent = PROG_TO_EDICT(pr_global_struct->newmis);
881	host_frametime = 0.05;
882	pr_global_struct->newmis = 0;
883
884	SV_RunEntity (ent);
885}
886
887/*
888================
889SV_Physics
890
891================
892*/
893void SV_Physics (void)
894{
895	int		i;
896	edict_t	*ent;
897	static double	old_time;
898
899// don't bother running a frame if sys_ticrate seconds haven't passed
900	host_frametime = realtime - old_time;
901	if (host_frametime < sv_mintic.value)
902		return;
903	if (host_frametime > sv_maxtic.value)
904		host_frametime = sv_maxtic.value;
905	old_time = realtime;
906
907	pr_global_struct->frametime = host_frametime;
908
909	SV_ProgStartFrame ();
910
911//
912// treat each object in turn
913// even the world gets a chance to think
914//
915	ent = sv.edicts;
916	for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
917	{
918		if (ent->free)
919			continue;
920
921		if (pr_global_struct->force_retouch)
922			SV_LinkEdict (ent, true);	// force retouch even for stationary
923
924		if (i > 0 && i <= MAX_CLIENTS)
925			continue;		// clients are run directly from packets
926
927		SV_RunEntity (ent);
928		SV_RunNewmis ();
929	}
930
931	if (pr_global_struct->force_retouch)
932		pr_global_struct->force_retouch--;
933}
934
935void SV_SetMoveVars(void)
936{
937	movevars.gravity			= sv_gravity.value;
938	movevars.stopspeed		    = sv_stopspeed.value;
939	movevars.maxspeed			= sv_maxspeed.value;
940	movevars.spectatormaxspeed  = sv_spectatormaxspeed.value;
941	movevars.accelerate		    = sv_accelerate.value;
942	movevars.airaccelerate	    = sv_airaccelerate.value;
943	movevars.wateraccelerate	= sv_wateraccelerate.value;
944	movevars.friction			= sv_friction.value;
945	movevars.waterfriction	    = sv_waterfriction.value;
946	movevars.entgravity			= 1.0;
947}
948