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