1 // g_phys.c
2 
3 #include "g_local.h"
4 
5 /*
6 
7 
8 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.
9 
10 onground is set for toss objects when they come to a complete rest.  it is set for steping or walking objects
11 
12 doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
13 bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
14 corpses are SOLID_NOT and MOVETYPE_TOSS
15 crates are SOLID_BBOX and MOVETYPE_TOSS
16 walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
17 flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
18 
19 solid_edge items only clip against bsp models.
20 
21 */
22 
23 
24 /*
25 ============
26 SV_TestEntityPosition
27 
28 ============
29 */
SV_TestEntityPosition(edict_t * ent)30 edict_t	*SV_TestEntityPosition (edict_t *ent)
31 {
32 	trace_t	trace;
33 	int		mask;
34 
35 	if (ent->clipmask)
36 		mask = ent->clipmask;
37 	else
38 		mask = MASK_SOLID;
39 	trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, ent, mask);
40 
41 	if (trace.startsolid)
42 		return g_edicts;
43 
44 	return NULL;
45 }
46 
47 
48 /*
49 ================
50 SV_CheckVelocity
51 ================
52 */
SV_CheckVelocity(edict_t * ent)53 void SV_CheckVelocity (edict_t *ent)
54 {
55 	int		i;
56 
57 //
58 // bound velocity
59 //
60 	for (i=0 ; i<3 ; i++)
61 	{
62 		if (ent->velocity[i] > sv_maxvelocity->value)
63 			ent->velocity[i] = sv_maxvelocity->value;
64 		else if (ent->velocity[i] < -sv_maxvelocity->value)
65 			ent->velocity[i] = -sv_maxvelocity->value;
66 	}
67 }
68 
69 /*
70 =============
71 SV_RunThink
72 
73 Runs thinking code for this frame if necessary
74 =============
75 */
SV_RunThink(edict_t * ent)76 qboolean SV_RunThink (edict_t *ent)
77 {
78 	float	thinktime;
79 
80 	thinktime = ent->nextthink;
81 	if (thinktime <= 0)
82 		return true;
83 	if (thinktime > level.time+0.001)
84 		return true;
85 
86 	ent->nextthink = 0;
87 	if (!ent->think)
88 		gi.error ("NULL ent->think");
89 	ent->think (ent);
90 
91 	return false;
92 }
93 
94 /*
95 ==================
96 SV_Impact
97 
98 Two entities have touched, so run their touch functions
99 ==================
100 */
SV_Impact(edict_t * e1,trace_t * trace)101 void SV_Impact (edict_t *e1, trace_t *trace)
102 {
103 	edict_t		*e2;
104 //	cplane_t	backplane;
105 
106 	e2 = trace->ent;
107 
108 	if (e1->touch && e1->solid != SOLID_NOT)
109 		e1->touch (e1, e2, &trace->plane, trace->surface);
110 
111 	if (e2->touch && e2->solid != SOLID_NOT)
112 		e2->touch (e2, e1, NULL, NULL);
113 }
114 
115 
116 /*
117 ==================
118 ClipVelocity
119 
120 Slide off of the impacting object
121 returns the blocked flags (1 = floor, 2 = step / wall)
122 ==================
123 */
124 #define	STOP_EPSILON	0.1
125 
ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)126 int ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
127 {
128 	float	backoff;
129 	float	change;
130 	int		i, blocked;
131 
132 	blocked = 0;
133 	if (normal[2] > 0)
134 		blocked |= 1;		// floor
135 	if (!normal[2])
136 		blocked |= 2;		// step
137 
138 	backoff = DotProduct (in, normal) * overbounce;
139 
140 	for (i=0 ; i<3 ; i++)
141 	{
142 		change = normal[i]*backoff;
143 		out[i] = in[i] - change;
144 		if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
145 			out[i] = 0;
146 	}
147 
148 	return blocked;
149 }
150 
151 
152 /*
153 ============
154 SV_FlyMove
155 
156 The basic solid body movement clip that slides along multiple planes
157 Returns the clipflags if the velocity was modified (hit something solid)
158 1 = floor
159 2 = wall / step
160 4 = dead stop
161 ============
162 */
163 #define	MAX_CLIP_PLANES	5
SV_FlyMove(edict_t * ent,float time,int mask)164 int SV_FlyMove (edict_t *ent, float time, int mask)
165 {
166 	edict_t		*hit;
167 	int			bumpcount, numbumps;
168 	vec3_t		dir;
169 	float		d;
170 	int			numplanes;
171 	vec3_t		planes[MAX_CLIP_PLANES];
172 	vec3_t		primal_velocity, original_velocity, new_velocity;
173 	int			i, j;
174 	trace_t		trace;
175 	vec3_t		end;
176 	float		time_left;
177 	int			blocked;
178 
179 	numbumps = 4;
180 
181 	blocked = 0;
182 	VectorCopy (ent->velocity, original_velocity);
183 	VectorCopy (ent->velocity, primal_velocity);
184 	numplanes = 0;
185 
186 	time_left = time;
187 
188 	ent->groundentity = NULL;
189 	for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
190 	{
191 		for (i=0 ; i<3 ; i++)
192 			end[i] = ent->s.origin[i] + time_left * ent->velocity[i];
193 
194 		trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, mask);
195 
196 		if (trace.allsolid)
197 		{	// entity is trapped in another solid
198 			VectorCopy (vec3_origin, ent->velocity);
199 			return 3;
200 		}
201 
202 		if (trace.fraction > 0)
203 		{	// actually covered some distance
204 			VectorCopy (trace.endpos, ent->s.origin);
205 			VectorCopy (ent->velocity, original_velocity);
206 			numplanes = 0;
207 		}
208 
209 		if (trace.fraction == 1)
210 			 break;		// moved the entire distance
211 
212 		hit = trace.ent;
213 
214 		if (trace.plane.normal[2] > 0.7)
215 		{
216 			blocked |= 1;		// floor
217 			if ( hit->solid == SOLID_BSP)
218 			{
219 				ent->groundentity = hit;
220 				ent->groundentity_linkcount = hit->linkcount;
221 			}
222 		}
223 		if (!trace.plane.normal[2])
224 		{
225 			blocked |= 2;		// step
226 		}
227 
228 //
229 // run the impact function
230 //
231 		SV_Impact (ent, &trace);
232 		if (!ent->inuse)
233 			break;		// removed by the impact function
234 
235 
236 		time_left -= time_left * trace.fraction;
237 
238 	// cliped to another plane
239 		if (numplanes >= MAX_CLIP_PLANES)
240 		{	// this shouldn't really happen
241 			VectorCopy (vec3_origin, ent->velocity);
242 			return 3;
243 		}
244 
245 		VectorCopy (trace.plane.normal, planes[numplanes]);
246 		numplanes++;
247 
248 //
249 // modify original_velocity so it parallels all of the clip planes
250 //
251 		for (i=0 ; i<numplanes ; i++)
252 		{
253 			ClipVelocity (original_velocity, planes[i], new_velocity, 1);
254 
255 			for (j=0 ; j<numplanes ; j++)
256 				if ((j != i) && !VectorCompare (planes[i], planes[j]))
257 				{
258 					if (DotProduct (new_velocity, planes[j]) < 0)
259 						break;	// not ok
260 				}
261 			if (j == numplanes)
262 				break;
263 		}
264 
265 		if (i != numplanes)
266 		{	// go along this plane
267 			VectorCopy (new_velocity, ent->velocity);
268 		}
269 		else
270 		{	// go along the crease
271 			if (numplanes != 2)
272 			{
273 //				gi.dprintf ("clip velocity, numplanes == %i\n",numplanes);
274 				VectorCopy (vec3_origin, ent->velocity);
275 				return 7;
276 			}
277 			CrossProduct (planes[0], planes[1], dir);
278 			d = DotProduct (dir, ent->velocity);
279 			VectorScale (dir, d, ent->velocity);
280 		}
281 
282 //
283 // if original velocity is against the original velocity, stop dead
284 // to avoid tiny occilations in sloping corners
285 //
286 		if (DotProduct (ent->velocity, primal_velocity) <= 0)
287 		{
288 			VectorCopy (vec3_origin, ent->velocity);
289 			return blocked;
290 		}
291 	}
292 
293 	return blocked;
294 }
295 
296 
297 /*
298 ============
299 SV_AddGravity
300 
301 ============
302 */
SV_AddGravity(edict_t * ent)303 void SV_AddGravity (edict_t *ent)
304 {
305 	ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME;
306 }
307 
308 /*
309 ===============================================================================
310 
311 PUSHMOVE
312 
313 ===============================================================================
314 */
315 
316 /*
317 ============
318 SV_PushEntity
319 
320 Does not change the entities velocity at all
321 ============
322 */
SV_PushEntity(edict_t * ent,vec3_t push)323 trace_t SV_PushEntity (edict_t *ent, vec3_t push)
324 {
325 	trace_t	trace;
326 	vec3_t	start;
327 	vec3_t	end;
328 	int		mask;
329 
330 	VectorCopy (ent->s.origin, start);
331 	VectorAdd (start, push, end);
332 
333 retry:
334 	if (ent->clipmask)
335 		mask = ent->clipmask;
336 	else
337 		mask = MASK_SOLID;
338 
339 	trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask);
340 
341 	VectorCopy (trace.endpos, ent->s.origin);
342 	gi.linkentity (ent);
343 
344 	if (trace.fraction != 1.0)
345 	{
346 		SV_Impact (ent, &trace);
347 
348 		// if the pushed entity went away and the pusher is still there
349 		if (!trace.ent->inuse && ent->inuse)
350 		{
351 			// move the pusher back and try again
352 			VectorCopy (start, ent->s.origin);
353 			gi.linkentity (ent);
354 			goto retry;
355 		}
356 	}
357 
358 	if (ent->inuse)
359 		G_TouchTriggers (ent);
360 
361 	return trace;
362 }
363 
364 
365 typedef struct
366 {
367 	edict_t	*ent;
368 	vec3_t	origin;
369 	vec3_t	angles;
370 	float	deltayaw;
371 } pushed_t;
372 pushed_t	pushed[MAX_EDICTS], *pushed_p;
373 
374 edict_t	*obstacle;
375 
376 /*
377 ============
378 SV_Push
379 
380 Objects need to be moved back on a failed push,
381 otherwise riders would continue to slide.
382 ============
383 */
SV_Push(edict_t * pusher,vec3_t move,vec3_t amove)384 qboolean SV_Push (edict_t *pusher, vec3_t move, vec3_t amove)
385 {
386 	int			i, e;
387 	edict_t		*check, *block;
388 	vec3_t		mins, maxs;
389 	pushed_t	*p;
390 	vec3_t		org, org2, move2, forward, right, up;
391 
392 	// clamp the move to 1/8 units, so the position will
393 	// be accurate for client side prediction
394 	for (i=0 ; i<3 ; i++)
395 	{
396 		float	temp;
397 		temp = move[i]*8.0;
398 		if (temp > 0.0)
399 			temp += 0.5;
400 		else
401 			temp -= 0.5;
402 		move[i] = 0.125 * (int)temp;
403 	}
404 
405 	// find the bounding box
406 	for (i=0 ; i<3 ; i++)
407 	{
408 		mins[i] = pusher->absmin[i] + move[i];
409 		maxs[i] = pusher->absmax[i] + move[i];
410 	}
411 
412 // we need this for pushing things later
413 	VectorSubtract (vec3_origin, amove, org);
414 	AngleVectors (org, forward, right, up);
415 
416 // save the pusher's original position
417 	pushed_p->ent = pusher;
418 	VectorCopy (pusher->s.origin, pushed_p->origin);
419 	VectorCopy (pusher->s.angles, pushed_p->angles);
420 	if (pusher->client)
421 		pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW];
422 	pushed_p++;
423 
424 // move the pusher to it's final position
425 	VectorAdd (pusher->s.origin, move, pusher->s.origin);
426 	VectorAdd (pusher->s.angles, amove, pusher->s.angles);
427 	gi.linkentity (pusher);
428 
429 // see if any solid entities are inside the final position
430 	check = g_edicts+1;
431 	for (e = 1; e < globals.num_edicts; e++, check++)
432 	{
433 		if (!check->inuse)
434 			continue;
435 		if (check->movetype == MOVETYPE_PUSH
436 		|| check->movetype == MOVETYPE_STOP
437 		|| check->movetype == MOVETYPE_NONE
438 		|| check->movetype == MOVETYPE_NOCLIP)
439 			continue;
440 
441 		if (!check->area.prev)
442 			continue;		// not linked in anywhere
443 
444 	// if the entity is standing on the pusher, it will definitely be moved
445 		if (check->groundentity != pusher)
446 		{
447 			// see if the ent needs to be tested
448 			if ( check->absmin[0] >= maxs[0]
449 			|| check->absmin[1] >= maxs[1]
450 			|| check->absmin[2] >= maxs[2]
451 			|| check->absmax[0] <= mins[0]
452 			|| check->absmax[1] <= mins[1]
453 			|| check->absmax[2] <= mins[2] )
454 				continue;
455 
456 			// see if the ent's bbox is inside the pusher's final position
457 			if (!SV_TestEntityPosition (check))
458 				continue;
459 		}
460 
461 		if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher))
462 		{
463 			// move this entity
464 			pushed_p->ent = check;
465 			VectorCopy (check->s.origin, pushed_p->origin);
466 			VectorCopy (check->s.angles, pushed_p->angles);
467 			pushed_p++;
468 
469 			// try moving the contacted entity
470 			VectorAdd (check->s.origin, move, check->s.origin);
471 			if (check->client)
472 			{	// FIXME: doesn't rotate monsters?
473 				check->client->ps.pmove.delta_angles[YAW] += amove[YAW];
474 			}
475 
476 			// figure movement due to the pusher's amove
477 			VectorSubtract (check->s.origin, pusher->s.origin, org);
478 			org2[0] = DotProduct (org, forward);
479 			org2[1] = -DotProduct (org, right);
480 			org2[2] = DotProduct (org, up);
481 			VectorSubtract (org2, org, move2);
482 			VectorAdd (check->s.origin, move2, check->s.origin);
483 
484 			// may have pushed them off an edge
485 			if (check->groundentity != pusher)
486 				check->groundentity = NULL;
487 
488 			block = SV_TestEntityPosition (check);
489 			if (!block)
490 			{	// pushed ok
491 				gi.linkentity (check);
492 				// impact?
493 				continue;
494 			}
495 
496 			// if it is ok to leave in the old position, do it
497 			// this is only relevent for riding entities, not pushed
498 			// FIXME: this doesn't acount for rotation
499 			VectorSubtract (check->s.origin, move, check->s.origin);
500 			block = SV_TestEntityPosition (check);
501 			if (!block)
502 			{
503 				pushed_p--;
504 				continue;
505 			}
506 		}
507 
508 		// save off the obstacle so we can call the block function
509 		obstacle = check;
510 
511 		// move back any entities we already moved
512 		// go backwards, so if the same entity was pushed
513 		// twice, it goes back to the original position
514 		for (p=pushed_p-1 ; p>=pushed ; p--)
515 		{
516 			VectorCopy (p->origin, p->ent->s.origin);
517 			VectorCopy (p->angles, p->ent->s.angles);
518 			if (p->ent->client)
519 			{
520 				p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw;
521 			}
522 			gi.linkentity (p->ent);
523 		}
524 		return false;
525 	}
526 
527 //FIXME: is there a better way to handle this?
528 	// see if anything we moved has touched a trigger
529 	for (p=pushed_p-1 ; p>=pushed ; p--)
530 		G_TouchTriggers (p->ent);
531 
532 	return true;
533 }
534 
535 /*
536 ================
537 SV_Physics_Pusher
538 
539 Bmodel objects don't interact with each other, but
540 push all box objects
541 ================
542 */
SV_Physics_Pusher(edict_t * ent)543 void SV_Physics_Pusher (edict_t *ent)
544 {
545 	vec3_t		move, amove;
546 	edict_t		*part, *mv;
547 
548 	// if not a team captain, so movement will be handled elsewhere
549 	if ( ent->flags & FL_TEAMSLAVE)
550 		return;
551 
552 	// make sure all team slaves can move before commiting
553 	// any moves or calling any think functions
554 	// if the move is blocked, all moved objects will be backed out
555 //retry:
556 	pushed_p = pushed;
557 	for (part = ent ; part ; part=part->teamchain)
558 	{
559 		if (part->velocity[0] || part->velocity[1] || part->velocity[2] ||
560 			part->avelocity[0] || part->avelocity[1] || part->avelocity[2]
561 			)
562 		{	// object is moving
563 			VectorScale (part->velocity, FRAMETIME, move);
564 			VectorScale (part->avelocity, FRAMETIME, amove);
565 
566 			if (!SV_Push (part, move, amove))
567 				break;	// move was blocked
568 		}
569 	}
570 	if (pushed_p > &pushed[MAX_EDICTS])
571 		gi.error (ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted");
572 
573 	if (part)
574 	{
575 		// the move failed, bump all nextthink times and back out moves
576 		for (mv = ent ; mv ; mv=mv->teamchain)
577 		{
578 			if (mv->nextthink > 0)
579 				mv->nextthink += FRAMETIME;
580 		}
581 
582 		// if the pusher has a "blocked" function, call it
583 		// otherwise, just stay in place until the obstacle is gone
584 		if (part->blocked)
585 			part->blocked (part, obstacle);
586 #if 0
587 		// if the pushed entity went away and the pusher is still there
588 		if (!obstacle->inuse && part->inuse)
589 			goto retry;
590 #endif
591 	}
592 	else
593 	{
594 		// the move succeeded, so call all think functions
595 		for (part = ent ; part ; part=part->teamchain)
596 		{
597 			SV_RunThink (part);
598 		}
599 	}
600 }
601 
602 //==================================================================
603 
604 /*
605 =============
606 SV_Physics_None
607 
608 Non moving objects can only think
609 =============
610 */
SV_Physics_None(edict_t * ent)611 void SV_Physics_None (edict_t *ent)
612 {
613 // regular thinking
614 	SV_RunThink (ent);
615 }
616 
617 /*
618 =============
619 SV_Physics_Noclip
620 
621 A moving object that doesn't obey physics
622 =============
623 */
SV_Physics_Noclip(edict_t * ent)624 void SV_Physics_Noclip (edict_t *ent)
625 {
626 // regular thinking
627 	if (!SV_RunThink (ent))
628 		return;
629 
630 	VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
631 	VectorMA (ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin);
632 
633 	gi.linkentity (ent);
634 }
635 
636 /*
637 ==============================================================================
638 
639 TOSS / BOUNCE
640 
641 ==============================================================================
642 */
643 
644 /*
645 =============
646 SV_Physics_Toss
647 
648 Toss, bounce, and fly movement.  When onground, do nothing.
649 =============
650 */
SV_Physics_Toss(edict_t * ent)651 void SV_Physics_Toss (edict_t *ent)
652 {
653 	trace_t		trace;
654 	vec3_t		move;
655 	float		backoff;
656 	edict_t		*slave;
657 	qboolean	wasinwater;
658 	qboolean	isinwater;
659 	vec3_t		old_origin;
660 
661 // regular thinking
662 	SV_RunThink (ent);
663 
664 	// if not a team captain, so movement will be handled elsewhere
665 	if ( ent->flags & FL_TEAMSLAVE)
666 		return;
667 
668 	if (ent->velocity[2] > 0)
669 		ent->groundentity = NULL;
670 
671 // check for the groundentity going away
672 	if (ent->groundentity)
673 		if (!ent->groundentity->inuse)
674 			ent->groundentity = NULL;
675 
676 // if onground, return without moving
677 	if ( ent->groundentity )
678 		return;
679 
680 	VectorCopy (ent->s.origin, old_origin);
681 
682 	SV_CheckVelocity (ent);
683 
684 // add gravity
685 	if (ent->movetype != MOVETYPE_FLY
686 	&& ent->movetype != MOVETYPE_FLYMISSILE
687 	&& ent->movetype != MOVETYPE_MATRIXROCKET
688 /*	&& !(strcmp(ent->classname, "poopoo") == 0)*/)
689 		SV_AddGravity (ent);
690 
691 	//matrix rocket (pain styleee)
692 	if (ent->movetype == MOVETYPE_MATRIXROCKET)
693 		Matrix_Rocket_Think(ent);
694 // move angles
695 	VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
696 
697 // move origin
698 	VectorScale (ent->velocity, FRAMETIME, move);
699 	trace = SV_PushEntity (ent, move);
700 	if (!ent->inuse)
701 		return;
702 
703 	if (trace.fraction < 1)
704 	{
705 		if (ent->movetype == MOVETYPE_BOUNCE/* ||  (strcmp(ent->classname, "poopoo") == 0)*/)
706 			backoff = 1.5;
707 		else
708 			backoff = 1;
709 
710 		ClipVelocity (ent->velocity, trace.plane.normal, ent->velocity, backoff);
711 
712 	// stop if on ground
713 		if (trace.plane.normal[2] > 0.7)
714 		{
715 			if (ent->velocity[2] < 60 || ent->movetype != MOVETYPE_BOUNCE )
716 			{
717 				ent->groundentity = trace.ent;
718 				ent->groundentity_linkcount = trace.ent->linkcount;
719 				VectorCopy (vec3_origin, ent->velocity);
720 				VectorCopy (vec3_origin, ent->avelocity);
721 			}
722 		}
723 
724 //		if (ent->touch)
725 //			ent->touch (ent, trace.ent, &trace.plane, trace.surface);
726 	}
727 
728 // check for water transition
729 	wasinwater = (ent->watertype & MASK_WATER);
730 	ent->watertype = gi.pointcontents (ent->s.origin);
731 	isinwater = ent->watertype & MASK_WATER;
732 
733 	if (isinwater)
734 		ent->waterlevel = 1;
735 	else
736 		ent->waterlevel = 0;
737 
738 	if (!wasinwater && isinwater)
739 		gi.positioned_sound (old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
740 	else if (wasinwater && !isinwater)
741 		gi.positioned_sound (ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
742 
743 // move teamslaves
744 	for (slave = ent->teamchain; slave; slave = slave->teamchain)
745 	{
746 		VectorCopy (ent->s.origin, slave->s.origin);
747 		gi.linkentity (slave);
748 	}
749 }
750 
751 /*
752 ===============================================================================
753 
754 STEPPING MOVEMENT
755 
756 ===============================================================================
757 */
758 
759 /*
760 =============
761 SV_Physics_Step
762 
763 Monsters freefall when they don't have a ground entity, otherwise
764 all movement is done with discrete steps.
765 
766 This is also used for objects that have become still on the ground, but
767 will fall if the floor is pulled out from under them.
768 FIXME: is this true?
769 =============
770 */
771 
772 //FIXME: hacked in for E3 demo
773 #define	sv_stopspeed		100
774 #define sv_friction			6
775 #define sv_waterfriction	1
776 
SV_AddRotationalFriction(edict_t * ent)777 void SV_AddRotationalFriction (edict_t *ent)
778 {
779 	int		n;
780 	float	adjustment;
781 
782 	VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
783 	adjustment = FRAMETIME * sv_stopspeed * sv_friction;
784 	for (n = 0; n < 3; n++)
785 	{
786 		if (ent->avelocity[n] > 0)
787 		{
788 			ent->avelocity[n] -= adjustment;
789 			if (ent->avelocity[n] < 0)
790 				ent->avelocity[n] = 0;
791 		}
792 		else
793 		{
794 			ent->avelocity[n] += adjustment;
795 			if (ent->avelocity[n] > 0)
796 				ent->avelocity[n] = 0;
797 		}
798 	}
799 }
800 
SV_Physics_Step(edict_t * ent)801 void SV_Physics_Step (edict_t *ent)
802 {
803 	qboolean	wasonground;
804 	qboolean	hitsound = false;
805 	float		*vel;
806 	float		speed, newspeed, control;
807 	float		friction;
808 	edict_t		*groundentity;
809 	int			mask;
810 
811 	// airborn monsters should always check for ground
812 	if (!ent->groundentity)
813 		M_CheckGround (ent);
814 
815 	groundentity = ent->groundentity;
816 
817 	SV_CheckVelocity (ent);
818 
819 	if (groundentity)
820 		wasonground = true;
821 	else
822 		wasonground = false;
823 
824 	if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
825 		SV_AddRotationalFriction (ent);
826 
827 	// add gravity except:
828 	//   flying monsters
829 	//   swimming monsters who are in the water
830 	if (! wasonground)
831 		if (!(ent->flags & FL_FLY))
832 			if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2)))
833 			{
834 				if (ent->velocity[2] < sv_gravity->value*-0.1)
835 					hitsound = true;
836 				if (ent->waterlevel == 0)
837 					SV_AddGravity (ent);
838 			}
839 
840 	// friction for flying monsters that have been given vertical velocity
841 	if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
842 	{
843 		speed = fabs(ent->velocity[2]);
844 		control = speed < sv_stopspeed ? sv_stopspeed : speed;
845 		friction = sv_friction/3;
846 		newspeed = speed - (FRAMETIME * control * friction);
847 		if (newspeed < 0)
848 			newspeed = 0;
849 		newspeed /= speed;
850 		ent->velocity[2] *= newspeed;
851 	}
852 
853 	// friction for flying monsters that have been given vertical velocity
854 	if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
855 	{
856 		speed = fabs(ent->velocity[2]);
857 		control = speed < sv_stopspeed ? sv_stopspeed : speed;
858 		newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel);
859 		if (newspeed < 0)
860 			newspeed = 0;
861 		newspeed /= speed;
862 		ent->velocity[2] *= newspeed;
863 	}
864 
865 	if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0])
866 	{
867 		// apply friction
868 		// let dead monsters who aren't completely onground slide
869 		if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY)))
870 			if (!(ent->health <= 0.0 && !M_CheckBottom(ent)))
871 			{
872 				vel = ent->velocity;
873 				speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
874 				if (speed)
875 				{
876 					friction = sv_friction;
877 
878 					control = speed < sv_stopspeed ? sv_stopspeed : speed;
879 					newspeed = speed - FRAMETIME*control*friction;
880 
881 					if (newspeed < 0)
882 						newspeed = 0;
883 					newspeed /= speed;
884 
885 					vel[0] *= newspeed;
886 					vel[1] *= newspeed;
887 				}
888 			}
889 
890 		if (ent->svflags & SVF_MONSTER)
891 			mask = MASK_MONSTERSOLID;
892 		else
893 			mask = MASK_SOLID;
894 		SV_FlyMove (ent, FRAMETIME, mask);
895 
896 		gi.linkentity (ent);
897 		G_TouchTriggers (ent);
898 		if (!ent->inuse)
899 			return;
900 
901 		if (ent->groundentity)
902 			if (!wasonground)
903 				if (hitsound)
904 					gi.sound (ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0);
905 	}
906 
907 // regular thinking
908 	SV_RunThink (ent);
909 }
910 
911 //============================================================================
912 /*
913 ================
914 G_RunEntity
915 
916 ================
917 */
G_RunEntity(edict_t * ent)918 void G_RunEntity (edict_t *ent)
919 {
920 	if (ent->prethink)
921 		ent->prethink (ent);
922 	if (Q_stricmp(ent->classname, "speedwave") == 0)
923 		SpeedWaveThink(ent);
924 
925 	switch ( (int)ent->movetype)
926 	{
927 	case MOVETYPE_PUSH:
928 	case MOVETYPE_STOP:
929 		SV_Physics_Pusher (ent);
930 		break;
931 	case MOVETYPE_NONE:
932 		SV_Physics_None (ent);
933 		break;
934 	case MOVETYPE_NOCLIP:
935 		SV_Physics_Noclip (ent);
936 		break;
937 	case MOVETYPE_STEP:
938 		SV_Physics_Step (ent);
939 		break;
940 	case MOVETYPE_TOSS:
941 	case MOVETYPE_BOUNCE:
942 	case MOVETYPE_FLY:
943 	case MOVETYPE_FLYMISSILE:
944 	case MOVETYPE_MATRIXROCKET:
945 		//matrix rocket
946 		SV_Physics_Toss (ent);
947 		break;
948 	default:
949 		gi.error ("SV_Physics: bad movetype %i", (int)ent->movetype);
950 	}
951 }
952