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