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