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