File: game\m_move.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 // m_move.c -- monster movement
   21 
   22 #include "g_local.h"
   23 
   24 #define STEPSIZE        18
   25 
   26 /*
   27 =============
   28 M_CheckBottom
   29 
   30 Returns false if any part of the bottom of the entity is off an edge that
   31 is not a staircase.
   32 
   33 =============
   34 */
   35 int c_yes, c_no;
   36 
   37 qboolean M_CheckBottom (edict_t *ent)
   38 {
   39         vec3_t  mins, maxs, start, stop;
   40         trace_t trace;
   41         int             x, y;
   42         float   mid, bottom;
   43         
   44         VectorAdd (ent->s.origin, ent->mins, mins);
   45         VectorAdd (ent->s.origin, ent->maxs, maxs);
   46 
   47 // if all of the points under the corners are solid world, don't bother
   48 // with the tougher checks
   49 // the corners must be within 16 of the midpoint
   50         start[2] = mins[2] - 1;
   51         for     (x=0 ; x<=1 ; x++)
   52                 for     (y=0 ; y<=1 ; y++)
   53                 {
   54                         start[0] = x ? maxs[0] : mins[0];
   55                         start[1] = y ? maxs[1] : mins[1];
   56                         if (gi.pointcontents (start) != CONTENTS_SOLID)
   57                                 goto realcheck;
   58                 }
   59 
   60         c_yes++;
   61         return true;            // we got out easy
   62 
   63 realcheck:
   64         c_no++;
   65 //
   66 // check it for real...
   67 //
   68         start[2] = mins[2];
   69         
   70 // the midpoint must be within 16 of the bottom
   71         start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
   72         start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
   73         stop[2] = start[2] - 2*STEPSIZE;
   74         trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
   75 
   76         if (trace.fraction == 1.0)
   77                 return false;
   78         mid = bottom = trace.endpos[2];
   79         
   80 // the corners must be within 16 of the midpoint        
   81         for     (x=0 ; x<=1 ; x++)
   82                 for     (y=0 ; y<=1 ; y++)
   83                 {
   84                         start[0] = stop[0] = x ? maxs[0] : mins[0];
   85                         start[1] = stop[1] = y ? maxs[1] : mins[1];
   86                         
   87                         trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
   88                         
   89                         if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
   90                                 bottom = trace.endpos[2];
   91                         if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
   92                                 return false;
   93                 }
   94 
   95         c_yes++;
   96         return true;
   97 }
   98 
   99 
  100 /*
  101 =============
  102 SV_movestep
  103 
  104 Called by monster program code.
  105 The move will be adjusted for slopes and stairs, but if the move isn't
  106 possible, no move is done, false is returned, and
  107 pr_global_struct->trace_normal is set to the normal of the blocking wall
  108 =============
  109 */
  110 //FIXME since we need to test end position contents here, can we avoid doing
  111 //it again later in catagorize position?
  112 qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
  113 {
  114         float           dz;
  115         vec3_t          oldorg, neworg, end;
  116         trace_t         trace;
  117         int                     i;
  118         float           stepsize;
  119         vec3_t          test;
  120         int                     contents;
  121 
  122 // try the move 
  123         VectorCopy (ent->s.origin, oldorg);
  124         VectorAdd (ent->s.origin, move, neworg);
  125 
  126 // flying monsters don't step up
  127         if ( ent->flags & (FL_SWIM | FL_FLY) )
  128         {
  129         // try one move with vertical motion, then one without
  130                 for (i=0 ; i<2 ; i++)
  131                 {
  132                         VectorAdd (ent->s.origin, move, neworg);
  133                         if (i == 0 && ent->enemy)
  134                         {
  135                                 if (!ent->goalentity)
  136                                         ent->goalentity = ent->enemy;
  137                                 dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
  138                                 if (ent->goalentity->client)
  139                                 {
  140                                         if (dz > 40)
  141                                                 neworg[2] -= 8;
  142                                         if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
  143                                                 if (dz < 30)
  144                                                         neworg[2] += 8;
  145                                 }
  146                                 else
  147                                 {
  148                                         if (dz > 8)
  149                                                 neworg[2] -= 8;
  150                                         else if (dz > 0)
  151                                                 neworg[2] -= dz;
  152                                         else if (dz < -8)
  153                                                 neworg[2] += 8;
  154                                         else
  155                                                 neworg[2] += dz;
  156                                 }
  157                         }
  158                         trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
  159         
  160                         // fly monsters don't enter water voluntarily
  161                         if (ent->flags & FL_FLY)
  162                         {
  163                                 if (!ent->waterlevel)
  164                                 {
  165                                         test[0] = trace.endpos[0];
  166                                         test[1] = trace.endpos[1];
  167                                         test[2] = trace.endpos[2] + ent->mins[2] + 1;
  168                                         contents = gi.pointcontents(test);
  169                                         if (contents & MASK_WATER)
  170                                                 return false;
  171                                 }
  172                         }
  173 
  174                         // swim monsters don't exit water voluntarily
  175                         if (ent->flags & FL_SWIM)
  176                         {
  177                                 if (ent->waterlevel < 2)
  178                                 {
  179                                         test[0] = trace.endpos[0];
  180                                         test[1] = trace.endpos[1];
  181                                         test[2] = trace.endpos[2] + ent->mins[2] + 1;
  182                                         contents = gi.pointcontents(test);
  183                                         if (!(contents & MASK_WATER))
  184                                                 return false;
  185                                 }
  186                         }
  187 
  188                         if (trace.fraction == 1)
  189                         {
  190                                 VectorCopy (trace.endpos, ent->s.origin);
  191                                 if (relink)
  192                                 {
  193                                         gi.linkentity (ent);
  194                                         G_TouchTriggers (ent);
  195                                 }
  196                                 return true;
  197                         }
  198                         
  199                         if (!ent->enemy)
  200                                 break;
  201                 }
  202                 
  203                 return false;
  204         }
  205 
  206 // push down from a step height above the wished position
  207         if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
  208                 stepsize = STEPSIZE;
  209         else
  210                 stepsize = 1;
  211 
  212         neworg[2] += stepsize;
  213         VectorCopy (neworg, end);
  214         end[2] -= stepsize*2;
  215 
  216         trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
  217 
  218         if (trace.allsolid)
  219                 return false;
  220 
  221         if (trace.startsolid)
  222         {
  223                 neworg[2] -= stepsize;
  224                 trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
  225                 if (trace.allsolid || trace.startsolid)
  226                         return false;
  227         }
  228 
  229 
  230         // don't go in to water
  231         if (ent->waterlevel == 0)
  232         {
  233                 test[0] = trace.endpos[0];
  234                 test[1] = trace.endpos[1];
  235                 test[2] = trace.endpos[2] + ent->mins[2] + 1;   
  236                 contents = gi.pointcontents(test);
  237 
  238                 if (contents & MASK_WATER)
  239                         return false;
  240         }
  241 
  242         if (trace.fraction == 1)
  243         {
  244         // if monster had the ground pulled out, go ahead and fall
  245                 if ( ent->flags & FL_PARTIALGROUND )
  246                 {
  247                         VectorAdd (ent->s.origin, move, ent->s.origin);
  248                         if (relink)
  249                         {
  250                                 gi.linkentity (ent);
  251                                 G_TouchTriggers (ent);
  252                         }
  253                         ent->groundentity = NULL;
  254                         return true;
  255                 }
  256         
  257                 return false;           // walked off an edge
  258         }
  259 
  260 // check point traces down for dangling corners
  261         VectorCopy (trace.endpos, ent->s.origin);
  262         
  263         if (!M_CheckBottom (ent))
  264         {
  265                 if ( ent->flags & FL_PARTIALGROUND )
  266                 {       // entity had floor mostly pulled out from underneath it
  267                         // and is trying to correct
  268                         if (relink)
  269                         {
  270                                 gi.linkentity (ent);
  271                                 G_TouchTriggers (ent);
  272                         }
  273                         return true;
  274                 }
  275                 VectorCopy (oldorg, ent->s.origin);
  276                 return false;
  277         }
  278 
  279         if ( ent->flags & FL_PARTIALGROUND )
  280         {
  281                 ent->flags &= ~FL_PARTIALGROUND;
  282         }
  283         ent->groundentity = trace.ent;
  284         ent->groundentity_linkcount = trace.ent->linkcount;
  285 
  286 // the move is ok
  287         if (relink)
  288         {
  289                 gi.linkentity (ent);
  290                 G_TouchTriggers (ent);
  291         }
  292         return true;
  293 }
  294 
  295 
  296 //============================================================================
  297 
  298 /*
  299 ===============
  300 M_ChangeYaw
  301 
  302 ===============
  303 */
  304 void M_ChangeYaw (edict_t *ent)
  305 {
  306         float   ideal;
  307         float   current;
  308         float   move;
  309         float   speed;
  310         
  311         current = anglemod(ent->s.angles[YAW]);
  312         ideal = ent->ideal_yaw;
  313 
  314         if (current == ideal)
  315                 return;
  316 
  317         move = ideal - current;
  318         speed = ent->yaw_speed;
  319         if (ideal > current)
  320         {
  321                 if (move >= 180)
  322                         move = move - 360;
  323         }
  324         else
  325         {
  326                 if (move <= -180)
  327                         move = move + 360;
  328         }
  329         if (move > 0)
  330         {
  331                 if (move > speed)
  332                         move = speed;
  333         }
  334         else
  335         {
  336                 if (move < -speed)
  337                         move = -speed;
  338         }
  339         
  340         ent->s.angles[YAW] = anglemod (current + move);
  341 }
  342 
  343 
  344 /*
  345 ======================
  346 SV_StepDirection
  347 
  348 Turns to the movement direction, and walks the current distance if
  349 facing it.
  350 
  351 ======================
  352 */
  353 qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
  354 {
  355         vec3_t          move, oldorigin;
  356         float           delta;
  357         
  358         ent->ideal_yaw = yaw;
  359         M_ChangeYaw (ent);
  360         
  361         yaw = yaw*M_PI*2 / 360;
  362         move[0] = cos(yaw)*dist;
  363         move[1] = sin(yaw)*dist;
  364         move[2] = 0;
  365 
  366         VectorCopy (ent->s.origin, oldorigin);
  367         if (SV_movestep (ent, move, false))
  368         {
  369                 delta = ent->s.angles[YAW] - ent->ideal_yaw;
  370                 if (delta > 45 && delta < 315)
  371                 {               // not turned far enough, so don't take the step
  372                         VectorCopy (oldorigin, ent->s.origin);
  373                 }
  374                 gi.linkentity (ent);
  375                 G_TouchTriggers (ent);
  376                 return true;
  377         }
  378         gi.linkentity (ent);
  379         G_TouchTriggers (ent);
  380         return false;
  381 }
  382 
  383 /*
  384 ======================
  385 SV_FixCheckBottom
  386 
  387 ======================
  388 */
  389 void SV_FixCheckBottom (edict_t *ent)
  390 {
  391         ent->flags |= FL_PARTIALGROUND;
  392 }
  393 
  394 
  395 
  396 /*
  397 ================
  398 SV_NewChaseDir
  399 
  400 ================
  401 */
  402 #define DI_NODIR        -1
  403 void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
  404 {
  405         float   deltax,deltay;
  406         float   d[3];
  407         float   tdir, olddir, turnaround;
  408 
  409         //FIXME: how did we get here with no enemy
  410         if (!enemy)
  411                 return;
  412 
  413         olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
  414         turnaround = anglemod(olddir - 180);
  415 
  416         deltax = enemy->s.origin[0] - actor->s.origin[0];
  417         deltay = enemy->s.origin[1] - actor->s.origin[1];
  418         if (deltax>10)
  419                 d[1]= 0;
  420         else if (deltax<-10)
  421                 d[1]= 180;
  422         else
  423                 d[1]= DI_NODIR;
  424         if (deltay<-10)
  425                 d[2]= 270;
  426         else if (deltay>10)
  427                 d[2]= 90;
  428         else
  429                 d[2]= DI_NODIR;
  430 
  431 // try direct route
  432         if (d[1] != DI_NODIR && d[2] != DI_NODIR)
  433         {
  434                 if (d[1] == 0)
  435                         tdir = d[2] == 90 ? 45 : 315;
  436                 else
  437                         tdir = d[2] == 90 ? 135 : 215;
  438                         
  439                 if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
  440                         return;
  441         }
  442 
  443 // try other directions
  444         if ( ((rand()&3) & 1) ||  abs(deltay)>abs(deltax))
  445         {
  446                 tdir=d[1];
  447                 d[1]=d[2];
  448                 d[2]=tdir;
  449         }
  450 
  451         if (d[1]!=DI_NODIR && d[1]!=turnaround 
  452         && SV_StepDirection(actor, d[1], dist))
  453                         return;
  454 
  455         if (d[2]!=DI_NODIR && d[2]!=turnaround
  456         && SV_StepDirection(actor, d[2], dist))
  457                         return;
  458 
  459 /* there is no direct path to the player, so pick another direction */
  460 
  461         if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
  462                         return;
  463 
  464         if (rand()&1)   /*randomly determine direction of search*/
  465         {
  466                 for (tdir=0 ; tdir<=315 ; tdir += 45)
  467                         if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
  468                                         return;
  469         }
  470         else
  471         {
  472                 for (tdir=315 ; tdir >=0 ; tdir -= 45)
  473                         if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
  474                                         return;
  475         }
  476 
  477         if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
  478                         return;
  479 
  480         actor->ideal_yaw = olddir;              // can't move
  481 
  482 // if a bridge was pulled out from underneath a monster, it may not have
  483 // a valid standing position at all
  484 
  485         if (!M_CheckBottom (actor))
  486                 SV_FixCheckBottom (actor);
  487 }
  488 
  489 /*
  490 ======================
  491 SV_CloseEnough
  492 
  493 ======================
  494 */
  495 qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
  496 {
  497         int             i;
  498         
  499         for (i=0 ; i<3 ; i++)
  500         {
  501                 if (goal->absmin[i] > ent->absmax[i] + dist)
  502                         return false;
  503                 if (goal->absmax[i] < ent->absmin[i] - dist)
  504                         return false;
  505         }
  506         return true;
  507 }
  508 
  509 
  510 /*
  511 ======================
  512 M_MoveToGoal
  513 ======================
  514 */
  515 void M_MoveToGoal (edict_t *ent, float dist)
  516 {
  517         edict_t         *goal;
  518         
  519         goal = ent->goalentity;
  520 
  521         if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
  522                 return;
  523 
  524 // if the next step hits the enemy, return immediately
  525         if (ent->enemy &&  SV_CloseEnough (ent, ent->enemy, dist) )
  526                 return;
  527 
  528 // bump around...
  529         if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
  530         {
  531                 if (ent->inuse)
  532                         SV_NewChaseDir (ent, goal, dist);
  533         }
  534 }
  535 
  536 
  537 /*
  538 ===============
  539 M_walkmove
  540 ===============
  541 */
  542 qboolean M_walkmove (edict_t *ent, float yaw, float dist)
  543 {
  544         vec3_t  move;
  545         
  546         if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
  547                 return false;
  548 
  549         yaw = yaw*M_PI*2 / 360;
  550         
  551         move[0] = cos(yaw)*dist;
  552         move[1] = sin(yaw)*dist;
  553         move[2] = 0;
  554 
  555         return SV_movestep(ent, move, true);
  556 }
  557