File: game\g_phys.c

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