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