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->value)
82 			ent->velocity[i] = sv_maxvelocity->value;
83 		else if (ent->velocity[i] < -sv_maxvelocity->value)
84 			ent->velocity[i] = -sv_maxvelocity->value;
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 qboolean SV_RunThink (edict_t *ent)
96 {
97 	float	thinktime;
98 
99 	thinktime = ent->nextthink;
100 	if (thinktime <= 0)
101 		return true;
102 	if (thinktime > level.time+0.001)
103 		return true;
104 
105 	ent->nextthink = 0;
106 	if (!ent->think)
107 		gi.error ("NULL ent->think");
108 	ent->think (ent);
109 
110 	return false;
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 //	cplane_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 	VectorCopy (ent->velocity, original_velocity);
202 	VectorCopy (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 			VectorCopy (vec3_origin, ent->velocity);
218 			return 3;
219 		}
220 
221 		if (trace.fraction > 0)
222 		{	// actually covered some distance
223 			VectorCopy (trace.endpos, ent->s.origin);
224 			VectorCopy (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 			VectorCopy (vec3_origin, ent->velocity);
261 			return 3;
262 		}
263 
264 		VectorCopy (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) && !VectorCompare (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 			VectorCopy (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 				VectorCopy (vec3_origin, ent->velocity);
294 				return 7;
295 			}
296 			CrossProduct (planes[0], planes[1], dir);
297 			d = DotProduct (dir, ent->velocity);
298 			VectorScale (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 			VectorCopy (vec3_origin, 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->value * 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 	VectorCopy (ent->s.origin, start);
350 	VectorAdd (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 	if (trace.startsolid || trace.allsolid)
361 	{
362 		mask ^= CONTENTS_DEADMONSTER;
363 		trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask);
364 	}
365 
366 	VectorCopy (trace.endpos, ent->s.origin);
367 	gi.linkentity (ent);
368 
369 	if (trace.fraction != 1.0)
370 	{
371 		SV_Impact (ent, &trace);
372 
373 		// if the pushed entity went away and the pusher is still there
374 		if (!trace.ent->inuse && ent->inuse)
375 		{
376 			// move the pusher back and try again
377 			VectorCopy (start, ent->s.origin);
378 			gi.linkentity (ent);
379 			goto retry;
380 		}
381 	}
382 
383 	if (ent->inuse)
384 		G_TouchTriggers (ent);
385 
386 	return trace;
387 }
388 
389 
390 typedef struct
391 {
392 	edict_t	*ent;
393 	vec3_t	origin;
394 	vec3_t	angles;
395 	float	deltayaw;
396 } pushed_t;
397 pushed_t	pushed[MAX_EDICTS], *pushed_p;
398 
399 edict_t	*obstacle;
400 
401 /*
402 ============
403 SV_Push
404 
405 Objects need to be moved back on a failed push,
406 otherwise riders would continue to slide.
407 ============
408 */
SV_Push(edict_t * pusher,vec3_t move,vec3_t amove)409 qboolean SV_Push (edict_t *pusher, vec3_t move, vec3_t amove)
410 {
411 	int			i, e;
412 	edict_t		*check, *block;
413 	vec3_t		mins, maxs;
414 	pushed_t	*p;
415 	vec3_t		org, org2, move2, forward, right, up;
416 
417 	// clamp the move to 1/8 units, so the position will
418 	// be accurate for client side prediction
419 	for (i=0 ; i<3 ; i++)
420 	{
421 		float	temp;
422 		temp = move[i]*8.0;
423 		if (temp > 0.0)
424 			temp += 0.5;
425 		else
426 			temp -= 0.5;
427 		move[i] = 0.125 * (int)temp;
428 	}
429 
430 	// find the bounding box
431 	for (i=0 ; i<3 ; i++)
432 	{
433 		mins[i] = pusher->absmin[i] + move[i];
434 		maxs[i] = pusher->absmax[i] + move[i];
435 	}
436 
437 // we need this for pushing things later
438 	VectorSubtract (vec3_origin, amove, org);
439 	AngleVectors (org, forward, right, up);
440 
441 // save the pusher's original position
442 	pushed_p->ent = pusher;
443 	VectorCopy (pusher->s.origin, pushed_p->origin);
444 	VectorCopy (pusher->s.angles, pushed_p->angles);
445 	if (pusher->client)
446 		pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW];
447 	pushed_p++;
448 
449 // move the pusher to it's final position
450 	VectorAdd (pusher->s.origin, move, pusher->s.origin);
451 	VectorAdd (pusher->s.angles, amove, pusher->s.angles);
452 	gi.linkentity (pusher);
453 
454 // see if any solid entities are inside the final position
455 	check = g_edicts+1;
456 	for (e = 1; e < globals.num_edicts; e++, check++)
457 	{
458 		if (!check->inuse)
459 			continue;
460 		if (check->movetype == MOVETYPE_PUSH
461 		|| check->movetype == MOVETYPE_STOP
462 		|| check->movetype == MOVETYPE_NONE
463 		|| check->movetype == MOVETYPE_NOCLIP)
464 			continue;
465 
466 		if (!check->area.prev)
467 			continue;		// not linked in anywhere
468 
469 	// if the entity is standing on the pusher, it will definitely be moved
470 		if (check->groundentity != pusher)
471 		{
472 			// see if the ent needs to be tested
473 			if ( check->absmin[0] >= maxs[0]
474 			|| check->absmin[1] >= maxs[1]
475 			|| check->absmin[2] >= maxs[2]
476 			|| check->absmax[0] <= mins[0]
477 			|| check->absmax[1] <= mins[1]
478 			|| check->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 		if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher))
487 		{
488 			// move this entity
489 			pushed_p->ent = check;
490 			VectorCopy (check->s.origin, pushed_p->origin);
491 			VectorCopy (check->s.angles, pushed_p->angles);
492 			pushed_p++;
493 
494 			// try moving the contacted entity
495 			VectorAdd (check->s.origin, move, check->s.origin);
496 			if (check->client)
497 			{	// FIXME: doesn't rotate monsters?
498 				check->client->ps.pmove.delta_angles[YAW] += amove[YAW];
499 			}
500 
501 			// figure movement due to the pusher's amove
502 			VectorSubtract (check->s.origin, pusher->s.origin, org);
503 			org2[0] = DotProduct (org, forward);
504 			org2[1] = -DotProduct (org, right);
505 			org2[2] = DotProduct (org, up);
506 			VectorSubtract (org2, org, move2);
507 			VectorAdd (check->s.origin, move2, check->s.origin);
508 
509 			// may have pushed them off an edge
510 			if (check->groundentity != pusher)
511 				check->groundentity = NULL;
512 
513 			block = SV_TestEntityPosition (check);
514 			if (!block)
515 			{	// pushed ok
516 				gi.linkentity (check);
517 				// impact?
518 				continue;
519 			}
520 
521 			// if it is ok to leave in the old position, do it
522 			// this is only relevent for riding entities, not pushed
523 			// FIXME: this doesn't acount for rotation
524 			VectorSubtract (check->s.origin, move, check->s.origin);
525 			block = SV_TestEntityPosition (check);
526 			if (!block)
527 			{
528 				pushed_p--;
529 				continue;
530 			}
531 		}
532 
533 		// save off the obstacle so we can call the block function
534 		obstacle = check;
535 
536 		// move back any entities we already moved
537 		// go backwards, so if the same entity was pushed
538 		// twice, it goes back to the original position
539 		for (p=pushed_p-1 ; p>=pushed ; p--)
540 		{
541 			VectorCopy (p->origin, p->ent->s.origin);
542 			VectorCopy (p->angles, p->ent->s.angles);
543 			if (p->ent->client)
544 			{
545 				p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw;
546 			}
547 			gi.linkentity (p->ent);
548 		}
549 		return false;
550 	}
551 
552 //FIXME: is there a better way to handle this?
553 	// see if anything we moved has touched a trigger
554 	for (p=pushed_p-1 ; p>=pushed ; p--)
555 		G_TouchTriggers (p->ent);
556 
557 	return true;
558 }
559 
560 /*
561 ================
562 SV_Physics_Pusher
563 
564 Bmodel objects don't interact with each other, but
565 push all box objects
566 ================
567 */
SV_Physics_Pusher(edict_t * ent)568 void SV_Physics_Pusher (edict_t *ent)
569 {
570 	vec3_t		move, amove;
571 	edict_t		*part, *mv;
572 
573 	// if not a team captain, so movement will be handled elsewhere
574 	if ( ent->flags & FL_TEAMSLAVE)
575 		return;
576 
577 	// make sure all team slaves can move before commiting
578 	// any moves or calling any think functions
579 	// if the move is blocked, all moved objects will be backed out
580 //retry:
581 	pushed_p = pushed;
582 	for (part = ent ; part ; part=part->teamchain)
583 	{
584 		if (part->velocity[0] || part->velocity[1] || part->velocity[2] ||
585 			part->avelocity[0] || part->avelocity[1] || part->avelocity[2]
586 			)
587 		{	// object is moving
588 			VectorScale (part->velocity, FRAMETIME, move);
589 			VectorScale (part->avelocity, FRAMETIME, amove);
590 
591 			if (!SV_Push (part, move, amove))
592 				break;	// move was blocked
593 		}
594 	}
595 	if (pushed_p > &pushed[MAX_EDICTS])
596 		gi.error (ERR_FATAL, "pushed_p > &pushed[MAX_EDICTS], memory corrupted");
597 
598 	if (part)
599 	{
600 		// the move failed, bump all nextthink times and back out moves
601 		for (mv = ent ; mv ; mv=mv->teamchain)
602 		{
603 			if (mv->nextthink > 0)
604 				mv->nextthink += FRAMETIME;
605 		}
606 
607 		// if the pusher has a "blocked" function, call it
608 		// otherwise, just stay in place until the obstacle is gone
609 		if (part->blocked)
610 			part->blocked (part, obstacle);
611 #if 0
612 		// if the pushed entity went away and the pusher is still there
613 		if (!obstacle->inuse && part->inuse)
614 			goto retry;
615 #endif
616 	}
617 	else
618 	{
619 		// the move succeeded, so call all think functions
620 		for (part = ent ; part ; part=part->teamchain)
621 		{
622 			SV_RunThink (part);
623 		}
624 	}
625 }
626 
627 //==================================================================
628 
629 /*
630 =============
631 SV_Physics_None
632 
633 Non moving objects can only think
634 =============
635 */
SV_Physics_None(edict_t * ent)636 void SV_Physics_None (edict_t *ent)
637 {
638 // regular thinking
639 	SV_RunThink (ent);
640 }
641 
642 /*
643 =============
644 SV_Physics_Noclip
645 
646 A moving object that doesn't obey physics
647 =============
648 */
SV_Physics_Noclip(edict_t * ent)649 void SV_Physics_Noclip (edict_t *ent)
650 {
651 // regular thinking
652 	if (!SV_RunThink (ent))
653 		return;
654 
655 	VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
656 	VectorMA (ent->s.origin, FRAMETIME, ent->velocity, ent->s.origin);
657 
658 	gi.linkentity (ent);
659 }
660 
661 /*
662 ==============================================================================
663 
664 TOSS / BOUNCE
665 
666 ==============================================================================
667 */
668 
669 /*
670 =============
671 SV_Physics_Toss
672 
673 Toss, bounce, and fly movement.  When onground, do nothing.
674 =============
675 */
SV_Physics_Toss(edict_t * ent)676 void SV_Physics_Toss (edict_t *ent)
677 {
678 	trace_t		trace;
679 	vec3_t		move;
680 	float		backoff;
681 	edict_t		*slave;
682 	qboolean	wasinwater;
683 	qboolean	isinwater;
684 	vec3_t		old_origin;
685 
686 // regular thinking
687 	SV_RunThink (ent);
688 
689 	// if not a team captain, so movement will be handled elsewhere
690 	if ( ent->flags & FL_TEAMSLAVE)
691 		return;
692 
693 	if (ent->velocity[2] > 0)
694 		ent->groundentity = NULL;
695 
696 // check for the groundentity going away
697 	if (ent->groundentity)
698 		if (!ent->groundentity->inuse)
699 			ent->groundentity = NULL;
700 
701 // if onground, return without moving
702 	if ( ent->groundentity )
703 		return;
704 
705 	VectorCopy (ent->s.origin, old_origin);
706 
707 	SV_CheckVelocity (ent);
708 
709 // add gravity
710 	if (ent->movetype != MOVETYPE_FLY
711 	&& ent->movetype != MOVETYPE_FLYMISSILE)
712 		SV_AddGravity (ent);
713 
714 // move angles
715 	VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
716 
717 // move origin
718 	VectorScale (ent->velocity, FRAMETIME, move);
719 	trace = SV_PushEntity (ent, move);
720 	if (!ent->inuse)
721 		return;
722 
723 	if (trace.fraction < 1)
724 	{
725 		if (ent->movetype == MOVETYPE_BOUNCE)
726 			backoff = 1.5;
727 		else
728 			backoff = 1;
729 
730 		ClipVelocity (ent->velocity, trace.plane.normal, ent->velocity, backoff);
731 
732 	// stop if on ground
733 		if (trace.plane.normal[2] > 0.7)
734 		{
735 			if (ent->velocity[2] < 60 || ent->movetype != MOVETYPE_BOUNCE )
736 			{
737 				ent->groundentity = trace.ent;
738 				ent->groundentity_linkcount = trace.ent->linkcount;
739 				VectorCopy (vec3_origin, ent->velocity);
740 				VectorCopy (vec3_origin, ent->avelocity);
741 			}
742 		}
743 
744 //		if (ent->touch)
745 //			ent->touch (ent, trace.ent, &trace.plane, trace.surface);
746 	}
747 
748 // check for water transition
749 	wasinwater = (ent->watertype & MASK_WATER);
750 	ent->watertype = gi.pointcontents (ent->s.origin);
751 	isinwater = ent->watertype & MASK_WATER;
752 
753 	if (isinwater)
754 		ent->waterlevel = 1;
755 	else
756 		ent->waterlevel = 0;
757 
758 	if (!wasinwater && isinwater)
759 		gi.positioned_sound (old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
760 	else if (wasinwater && !isinwater)
761 		gi.positioned_sound (ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
762 
763 // move teamslaves
764 	for (slave = ent->teamchain; slave; slave = slave->teamchain)
765 	{
766 		VectorCopy (ent->s.origin, slave->s.origin);
767 		gi.linkentity (slave);
768 	}
769 }
770 
771 /*
772 ===============================================================================
773 
774 STEPPING MOVEMENT
775 
776 ===============================================================================
777 */
778 
779 /*
780 =============
781 SV_Physics_Step
782 
783 Monsters freefall when they don't have a ground entity, otherwise
784 all movement is done with discrete steps.
785 
786 This is also used for objects that have become still on the ground, but
787 will fall if the floor is pulled out from under them.
788 FIXME: is this true?
789 =============
790 */
791 
792 //FIXME: hacked in for E3 demo
793 #define	sv_stopspeed		100
794 #define sv_friction			6
795 #define sv_waterfriction	1
796 
SV_AddRotationalFriction(edict_t * ent)797 void SV_AddRotationalFriction (edict_t *ent)
798 {
799 	int		n;
800 	float	adjustment;
801 
802 	VectorMA (ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
803 	adjustment = FRAMETIME * sv_stopspeed * sv_friction;
804 	for (n = 0; n < 3; n++)
805 	{
806 		if (ent->avelocity[n] > 0)
807 		{
808 			ent->avelocity[n] -= adjustment;
809 			if (ent->avelocity[n] < 0)
810 				ent->avelocity[n] = 0;
811 		}
812 		else
813 		{
814 			ent->avelocity[n] += adjustment;
815 			if (ent->avelocity[n] > 0)
816 				ent->avelocity[n] = 0;
817 		}
818 	}
819 }
820 
SV_Physics_Step(edict_t * ent)821 void SV_Physics_Step (edict_t *ent)
822 {
823 	qboolean	wasonground;
824 	qboolean	hitsound = false;
825 	float		*vel;
826 	float		speed, newspeed, control;
827 	float		friction;
828 	edict_t		*groundentity;
829 	int			mask;
830 
831 	// airborn monsters should always check for ground
832 	if (!ent->groundentity)
833 		M_CheckGround (ent);
834 
835 	groundentity = ent->groundentity;
836 
837 	SV_CheckVelocity (ent);
838 
839 	if (groundentity)
840 		wasonground = true;
841 	else
842 		wasonground = false;
843 
844 	if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
845 		SV_AddRotationalFriction (ent);
846 
847 	// add gravity except:
848 	//   flying monsters
849 	//   swimming monsters who are in the water
850 	if (! wasonground)
851 		if (!(ent->flags & FL_FLY))
852 			if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2)))
853 			{
854 				if (ent->velocity[2] < sv_gravity->value*-0.1)
855 					hitsound = true;
856 				if (ent->waterlevel == 0)
857 					SV_AddGravity (ent);
858 			}
859 
860 	// friction for flying monsters that have been given vertical velocity
861 	if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
862 	{
863 		speed = fabs(ent->velocity[2]);
864 		control = speed < sv_stopspeed ? sv_stopspeed : speed;
865 		friction = sv_friction/3;
866 		newspeed = speed - (FRAMETIME * control * friction);
867 		if (newspeed < 0)
868 			newspeed = 0;
869 		newspeed /= speed;
870 		ent->velocity[2] *= newspeed;
871 	}
872 
873 	// friction for flying monsters that have been given vertical velocity
874 	if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
875 	{
876 		speed = fabs(ent->velocity[2]);
877 		control = speed < sv_stopspeed ? sv_stopspeed : speed;
878 		newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel);
879 		if (newspeed < 0)
880 			newspeed = 0;
881 		newspeed /= speed;
882 		ent->velocity[2] *= newspeed;
883 	}
884 
885 	if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0])
886 	{
887 		// apply friction
888 		// let dead monsters who aren't completely onground slide
889 		if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY)))
890 			if (!(ent->health <= 0.0 && !M_CheckBottom(ent)))
891 			{
892 				vel = ent->velocity;
893 				speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
894 				if (speed)
895 				{
896 					friction = sv_friction;
897 
898 					control = speed < sv_stopspeed ? sv_stopspeed : speed;
899 					newspeed = speed - FRAMETIME*control*friction;
900 
901 					if (newspeed < 0)
902 						newspeed = 0;
903 					newspeed /= speed;
904 
905 					vel[0] *= newspeed;
906 					vel[1] *= newspeed;
907 				}
908 			}
909 
910 		if (ent->svflags & SVF_MONSTER)
911 			mask = MASK_MONSTERSOLID;
912 		else
913 			mask = MASK_SOLID;
914 		SV_FlyMove (ent, FRAMETIME, mask);
915 
916 		gi.linkentity (ent);
917 		G_TouchTriggers (ent);
918 		if (!ent->inuse)
919 			return;
920 
921 		if (ent->groundentity)
922 			if (!wasonground)
923 				if (hitsound)
924 					gi.sound (ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0);
925 	}
926 
927 // regular thinking
928 	SV_RunThink (ent);
929 }
930 
931 //============================================================================
932 /*
933 ================
934 G_RunEntity
935 
936 ================
937 */
G_RunEntity(edict_t * ent)938 void G_RunEntity (edict_t *ent)
939 {
940 	if (ent->prethink)
941 		ent->prethink (ent);
942 
943 	switch ( (int)ent->movetype)
944 	{
945 	case MOVETYPE_PUSH:
946 	case MOVETYPE_STOP:
947 		SV_Physics_Pusher (ent);
948 		break;
949 	case MOVETYPE_NONE:
950 		SV_Physics_None (ent);
951 		break;
952 	case MOVETYPE_NOCLIP:
953 		SV_Physics_Noclip (ent);
954 		break;
955 	case MOVETYPE_STEP:
956 		SV_Physics_Step (ent);
957 		break;
958 	case MOVETYPE_TOSS:
959 	case MOVETYPE_BOUNCE:
960 	case MOVETYPE_FLY:
961 	case MOVETYPE_FLYMISSILE:
962 		SV_Physics_Toss (ent);
963 		break;
964 	default:
965 		gi.error ("SV_Physics: bad movetype %i", (int)ent->movetype);
966 	}
967 }
968