m_move.c (11476B)
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 }