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