p_map.cpp (30931B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #include "Precompiled.h" 30 #include "globaldata.h" 31 32 #include <stdlib.h> 33 34 #include "m_bbox.h" 35 #include "m_random.h" 36 #include "i_system.h" 37 38 #include "doomdef.h" 39 #include "p_local.h" 40 41 #include "s_sound.h" 42 43 // State. 44 #include "doomstat.h" 45 #include "r_state.h" 46 // Data. 47 #include "sounds.h" 48 49 #include "Main.h" 50 51 52 // If "floatok" true, move would be ok 53 // if within "tmfloorz - tmceilingz". 54 55 56 // keep track of the line that lowers the ceiling, 57 // so missiles don't explode against sky hack walls 58 59 // keep track of special ::g->lines as they are hit, 60 // but don't process them until the move is proven valid 61 62 63 64 65 // 66 // TELEPORT MOVE 67 // 68 69 // 70 // PIT_StompThing 71 // 72 qboolean PIT_StompThing (mobj_t* thing) 73 { 74 fixed_t blockdist; 75 76 if (!(thing->flags & MF_SHOOTABLE) ) 77 return true; 78 79 blockdist = thing->radius + ::g->tmthing->radius; 80 81 if ( abs(thing->x - ::g->tmx) >= blockdist 82 || abs(thing->y - ::g->tmy) >= blockdist ) 83 { 84 // didn't hit it 85 return true; 86 } 87 88 // don't clip against self 89 if (thing == ::g->tmthing) 90 return true; 91 92 // monsters don't stomp things except on boss level 93 if ( !::g->tmthing->player && ::g->gamemap != 30) 94 return false; 95 96 P_DamageMobj (thing, ::g->tmthing, ::g->tmthing, 10000); 97 98 return true; 99 } 100 101 102 // 103 // P_TeleportMove 104 // 105 qboolean 106 P_TeleportMove 107 ( mobj_t* thing, 108 fixed_t x, 109 fixed_t y ) 110 { 111 int xl; 112 int xh; 113 int yl; 114 int yh; 115 int bx; 116 int by; 117 118 subsector_t* newsubsec; 119 120 // kill anything occupying the position 121 ::g->tmthing = thing; 122 ::g->tmflags = thing->flags; 123 124 ::g->tmx = x; 125 ::g->tmy = y; 126 127 ::g->tmbbox[BOXTOP] = y + ::g->tmthing->radius; 128 ::g->tmbbox[BOXBOTTOM] = y - ::g->tmthing->radius; 129 ::g->tmbbox[BOXRIGHT] = x + ::g->tmthing->radius; 130 ::g->tmbbox[BOXLEFT] = x - ::g->tmthing->radius; 131 132 newsubsec = R_PointInSubsector (x,y); 133 ::g->ceilingline = NULL; 134 135 // The base floor/ceiling is from the subsector 136 // that contains the point. 137 // Any contacted ::g->lines the step closer together 138 // will adjust them. 139 ::g->tmfloorz = ::g->tmdropoffz = newsubsec->sector->floorheight; 140 ::g->tmceilingz = newsubsec->sector->ceilingheight; 141 142 ::g->validcount++; 143 ::g->numspechit = 0; 144 145 // stomp on any things contacted 146 xl = (::g->tmbbox[BOXLEFT] - ::g->bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; 147 xh = (::g->tmbbox[BOXRIGHT] - ::g->bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; 148 yl = (::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; 149 yh = (::g->tmbbox[BOXTOP] - ::g->bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; 150 151 for (bx=xl ; bx<=xh ; bx++) 152 for (by=yl ; by<=yh ; by++) 153 if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) 154 return false; 155 156 // the move is ok, 157 // so link the thing into its new position 158 P_UnsetThingPosition (thing); 159 160 thing->floorz = ::g->tmfloorz; 161 thing->ceilingz = ::g->tmceilingz; 162 thing->x = x; 163 thing->y = y; 164 165 P_SetThingPosition (thing); 166 167 return true; 168 } 169 170 171 // 172 // MOVEMENT ITERATOR FUNCTIONS 173 // 174 175 176 // 177 // PIT_CheckLine 178 // Adjusts ::g->tmfloorz and ::g->tmceilingz as ::g->lines are contacted 179 // 180 qboolean PIT_CheckLine (line_t* ld) 181 { 182 if (::g->tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] 183 || ::g->tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] 184 || ::g->tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] 185 || ::g->tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] ) 186 return true; 187 188 if (P_BoxOnLineSide (::g->tmbbox, ld) != -1) 189 return true; 190 191 // A line has been hit 192 193 // The moving thing's destination position will cross 194 // the given line. 195 // If this should not be allowed, return false. 196 // If the line is special, keep track of it 197 // to process later if the move is proven ok. 198 // NOTE: specials are NOT sorted by order, 199 // so two special ::g->lines that are only 8 pixels apart 200 // could be crossed in either order. 201 202 if (!ld->backsector) 203 return false; // one sided line 204 205 if (!(::g->tmthing->flags & MF_MISSILE) ) 206 { 207 if ( ld->flags & ML_BLOCKING ) 208 return false; // explicitly blocking everything 209 210 if ( !::g->tmthing->player && ld->flags & ML_BLOCKMONSTERS ) 211 return false; // block monsters only 212 } 213 214 // set ::g->openrange, ::g->opentop, ::g->openbottom 215 P_LineOpening (ld); 216 217 // adjust floor / ceiling heights 218 if (::g->opentop < ::g->tmceilingz) 219 { 220 ::g->tmceilingz = ::g->opentop; 221 ::g->ceilingline = ld; 222 } 223 224 if (::g->openbottom > ::g->tmfloorz) 225 ::g->tmfloorz = ::g->openbottom; 226 227 if (::g->lowfloor < ::g->tmdropoffz) 228 ::g->tmdropoffz = ::g->lowfloor; 229 230 // if contacted a special line, add it to the list 231 if (ld->special && ::g->numspechit < MAXSPECIALCROSS ) 232 { 233 ::g->spechit[::g->numspechit] = ld; 234 ::g->numspechit++; 235 } 236 237 return true; 238 } 239 240 // 241 // PIT_CheckThing 242 // 243 qboolean PIT_CheckThing (mobj_t* thing) 244 { 245 fixed_t blockdist; 246 qboolean solid; 247 int damage; 248 249 if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE) )) 250 return true; 251 252 blockdist = thing->radius + ::g->tmthing->radius; 253 254 if ( abs(thing->x - ::g->tmx) >= blockdist 255 || abs(thing->y - ::g->tmy) >= blockdist ) 256 { 257 // didn't hit it 258 return true; 259 } 260 261 // don't clip against self 262 if (thing == ::g->tmthing) 263 return true; 264 265 // check for skulls slamming into things 266 if (::g->tmthing->flags & MF_SKULLFLY) 267 { 268 damage = ((P_Random()%8)+1)*::g->tmthing->info->damage; 269 270 P_DamageMobj (thing, ::g->tmthing, ::g->tmthing, damage); 271 272 ::g->tmthing->flags &= ~MF_SKULLFLY; 273 ::g->tmthing->momx = ::g->tmthing->momy = ::g->tmthing->momz = 0; 274 275 P_SetMobjState (::g->tmthing, (statenum_t)::g->tmthing->info->spawnstate); 276 277 return false; // stop moving 278 } 279 280 281 // missiles can hit other things 282 if (::g->tmthing->flags & MF_MISSILE) 283 { 284 // see if it went over / under 285 if (::g->tmthing->z > thing->z + thing->height) 286 return true; // overhead 287 if (::g->tmthing->z+::g->tmthing->height < thing->z) 288 return true; // underneath 289 290 if (::g->tmthing->target && ( 291 ::g->tmthing->target->type == thing->type || 292 (::g->tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)|| 293 (::g->tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT) ) ) 294 { 295 // Don't hit same species as originator. 296 if (thing == ::g->tmthing->target) 297 return true; 298 299 if (thing->type != MT_PLAYER) 300 { 301 // Explode, but do no damage. 302 // Let ::g->players missile other ::g->players. 303 return false; 304 } 305 } 306 307 if (! (thing->flags & MF_SHOOTABLE) ) 308 { 309 // didn't do any damage 310 return !(thing->flags & MF_SOLID); 311 } 312 313 // damage / explode 314 damage = ((P_Random()%8)+1)*::g->tmthing->info->damage; 315 P_DamageMobj (thing, ::g->tmthing, ::g->tmthing->target, damage); 316 317 // don't traverse any more 318 return false; 319 } 320 321 // check for special pickup 322 if (thing->flags & MF_SPECIAL) 323 { 324 solid = thing->flags&MF_SOLID; 325 if (::g->tmflags&MF_PICKUP) 326 { 327 // can remove thing 328 P_TouchSpecialThing (thing, ::g->tmthing); 329 } 330 return !solid; 331 } 332 333 return !(thing->flags & MF_SOLID); 334 } 335 336 337 // 338 // MOVEMENT CLIPPING 339 // 340 341 // 342 // P_CheckPosition 343 // This is purely informative, nothing is modified 344 // (except things picked up). 345 // 346 // in: 347 // a mobj_t (can be valid or invalid) 348 // a position to be checked 349 // (doesn't need to be related to the mobj_t->x,y) 350 // 351 // during: 352 // special things are touched if MF_PICKUP 353 // early out on solid lines? 354 // 355 // out: 356 // newsubsec 357 // floorz 358 // ceilingz 359 // ::g->tmdropoffz 360 // the lowest point contacted 361 // (monsters won't move to a dropoff) 362 // speciallines[] 363 // numspeciallines 364 // 365 qboolean 366 P_CheckPosition 367 ( mobj_t* thing, 368 fixed_t x, 369 fixed_t y ) 370 { 371 int xl; 372 int xh; 373 int yl; 374 int yh; 375 int bx; 376 int by; 377 subsector_t* newsubsec; 378 379 ::g->tmthing = thing; 380 ::g->tmflags = thing->flags; 381 382 ::g->tmx = x; 383 ::g->tmy = y; 384 385 ::g->tmbbox[BOXTOP] = y + ::g->tmthing->radius; 386 ::g->tmbbox[BOXBOTTOM] = y - ::g->tmthing->radius; 387 ::g->tmbbox[BOXRIGHT] = x + ::g->tmthing->radius; 388 ::g->tmbbox[BOXLEFT] = x - ::g->tmthing->radius; 389 390 newsubsec = R_PointInSubsector (x,y); 391 ::g->ceilingline = NULL; 392 393 // The base floor / ceiling is from the subsector 394 // that contains the point. 395 // Any contacted ::g->lines the step closer together 396 // will adjust them. 397 ::g->tmfloorz = ::g->tmdropoffz = newsubsec->sector->floorheight; 398 ::g->tmceilingz = newsubsec->sector->ceilingheight; 399 400 ::g->validcount++; 401 ::g->numspechit = 0; 402 403 if ( ::g->tmflags & MF_NOCLIP ) 404 return true; 405 406 // Check things first, possibly picking things up. 407 // The bounding box is extended by MAXRADIUS 408 // because mobj_ts are grouped into mapblocks 409 // based on their origin point, and can overlap 410 // into adjacent blocks by up to MAXRADIUS units. 411 xl = (::g->tmbbox[BOXLEFT] - ::g->bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; 412 xh = (::g->tmbbox[BOXRIGHT] - ::g->bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; 413 yl = (::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; 414 yh = (::g->tmbbox[BOXTOP] - ::g->bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; 415 416 for (bx=xl ; bx<=xh ; bx++) 417 for (by=yl ; by<=yh ; by++) 418 if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) 419 return false; 420 421 // check ::g->lines 422 xl = (::g->tmbbox[BOXLEFT] - ::g->bmaporgx)>>MAPBLOCKSHIFT; 423 xh = (::g->tmbbox[BOXRIGHT] - ::g->bmaporgx)>>MAPBLOCKSHIFT; 424 yl = (::g->tmbbox[BOXBOTTOM] - ::g->bmaporgy)>>MAPBLOCKSHIFT; 425 yh = (::g->tmbbox[BOXTOP] - ::g->bmaporgy)>>MAPBLOCKSHIFT; 426 427 for (bx=xl ; bx<=xh ; bx++) 428 for (by=yl ; by<=yh ; by++) 429 if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) 430 return false; 431 432 return true; 433 } 434 435 436 // 437 // P_TryMove 438 // Attempt to move to a new position, 439 // crossing special ::g->lines unless MF_TELEPORT is set. 440 // 441 qboolean 442 P_TryMove 443 ( mobj_t* thing, 444 fixed_t x, 445 fixed_t y ) 446 { 447 fixed_t oldx; 448 fixed_t oldy; 449 int side; 450 int oldside; 451 line_t* ld; 452 453 ::g->floatok = false; 454 if (!P_CheckPosition (thing, x, y)) 455 return false; // solid wall or thing 456 457 if ( !(thing->flags & MF_NOCLIP) ) 458 { 459 if (::g->tmceilingz - ::g->tmfloorz < thing->height) 460 return false; // doesn't fit 461 462 ::g->floatok = true; 463 464 if ( !(thing->flags&MF_TELEPORT) 465 &&::g->tmceilingz - thing->z < thing->height) 466 return false; // mobj must lower itself to fit 467 468 if ( !(thing->flags&MF_TELEPORT) 469 && ::g->tmfloorz - thing->z > 24*FRACUNIT ) 470 return false; // too big a step up 471 472 if ( !(thing->flags&(MF_DROPOFF|MF_FLOAT)) 473 && ::g->tmfloorz - ::g->tmdropoffz > 24*FRACUNIT ) 474 return false; // don't stand over a dropoff 475 } 476 477 // the move is ok, 478 // so link the thing into its new position 479 P_UnsetThingPosition (thing); 480 481 oldx = thing->x; 482 oldy = thing->y; 483 thing->floorz = ::g->tmfloorz; 484 thing->ceilingz = ::g->tmceilingz; 485 thing->x = x; 486 thing->y = y; 487 488 P_SetThingPosition (thing); 489 490 // if any special ::g->lines were hit, do the effect 491 if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) ) 492 { 493 while (::g->numspechit--) 494 { 495 // see if the line was crossed 496 ld = ::g->spechit[::g->numspechit]; 497 side = P_PointOnLineSide (thing->x, thing->y, ld); 498 oldside = P_PointOnLineSide (oldx, oldy, ld); 499 if (side != oldside) 500 { 501 if (ld->special) 502 P_CrossSpecialLine (ld-::g->lines, oldside, thing); 503 } 504 } 505 } 506 507 return true; 508 } 509 510 511 // 512 // P_ThingHeightClip 513 // Takes a valid thing and adjusts the thing->floorz, 514 // thing->ceilingz, and possibly thing->z. 515 // This is called for all nearby monsters 516 // whenever a sector changes height. 517 // If the thing doesn't fit, 518 // the z will be set to the lowest value 519 // and false will be returned. 520 // 521 qboolean P_ThingHeightClip (mobj_t* thing) 522 { 523 qboolean onfloor; 524 525 onfloor = (thing->z == thing->floorz); 526 527 P_CheckPosition (thing, thing->x, thing->y); 528 // what about stranding a monster partially off an edge? 529 530 thing->floorz = ::g->tmfloorz; 531 thing->ceilingz = ::g->tmceilingz; 532 533 if (onfloor) 534 { 535 // walking monsters rise and fall with the floor 536 thing->z = thing->floorz; 537 } 538 else 539 { 540 // don't adjust a floating monster unless forced to 541 if (thing->z+thing->height > thing->ceilingz) 542 thing->z = thing->ceilingz - thing->height; 543 } 544 545 if (thing->ceilingz - thing->floorz < thing->height) 546 return false; 547 548 return true; 549 } 550 551 552 553 // 554 // SLIDE MOVE 555 // Allows the player to slide along any angled walls. 556 // 557 558 559 560 561 562 563 // 564 // P_HitSlideLine 565 // Adjusts the xmove / ymove 566 // so that the next move will slide along the wall. 567 // 568 void P_HitSlideLine (line_t* ld) 569 { 570 int side; 571 572 angle_t lineangle; 573 angle_t moveangle; 574 angle_t deltaangle; 575 576 fixed_t movelen; 577 fixed_t newlen; 578 579 580 if (ld->slopetype == ST_HORIZONTAL) 581 { 582 ::g->tmymove = 0; 583 return; 584 } 585 586 if (ld->slopetype == ST_VERTICAL) 587 { 588 ::g->tmxmove = 0; 589 return; 590 } 591 592 side = P_PointOnLineSide (::g->slidemo->x, ::g->slidemo->y, ld); 593 594 lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); 595 596 if (side == 1) 597 lineangle += ANG180; 598 599 moveangle = R_PointToAngle2 (0,0, ::g->tmxmove, ::g->tmymove); 600 deltaangle = moveangle-lineangle; 601 602 if (deltaangle > ANG180) 603 deltaangle += ANG180; 604 // I_Error ("SlideLine: ang>ANG180"); 605 606 lineangle >>= ANGLETOFINESHIFT; 607 deltaangle >>= ANGLETOFINESHIFT; 608 609 movelen = P_AproxDistance (::g->tmxmove, ::g->tmymove); 610 newlen = FixedMul (movelen, finecosine[deltaangle]); 611 612 ::g->tmxmove = FixedMul (newlen, finecosine[lineangle]); 613 ::g->tmymove = FixedMul (newlen, finesine[lineangle]); 614 } 615 616 617 // 618 // PTR_SlideTraverse 619 // 620 qboolean PTR_SlideTraverse (intercept_t* in) 621 { 622 line_t* li; 623 624 if (!in->isaline) 625 I_Error ("PTR_SlideTraverse: not a line?"); 626 627 li = in->d.line; 628 629 if ( ! (li->flags & ML_TWOSIDED) ) 630 { 631 if (P_PointOnLineSide (::g->slidemo->x, ::g->slidemo->y, li)) 632 { 633 // don't hit the back side 634 return true; 635 } 636 goto isblocking; 637 } 638 639 // set ::g->openrange, ::g->opentop, ::g->openbottom 640 P_LineOpening (li); 641 642 if (::g->openrange < ::g->slidemo->height) 643 goto isblocking; // doesn't fit 644 645 if (::g->opentop - ::g->slidemo->z < ::g->slidemo->height) 646 goto isblocking; // mobj is too high 647 648 if (::g->openbottom - ::g->slidemo->z > 24*FRACUNIT ) 649 goto isblocking; // too big a step up 650 651 // this line doesn't block movement 652 return true; 653 654 // the line does block movement, 655 // see if it is closer than best so far 656 isblocking: 657 if (in->frac < ::g->bestslidefrac) 658 { 659 ::g->secondslidefrac = ::g->bestslidefrac; 660 ::g->secondslideline = ::g->bestslideline; 661 ::g->bestslidefrac = in->frac; 662 ::g->bestslideline = li; 663 } 664 665 return false; // stop 666 } 667 668 669 670 // 671 // P_SlideMove 672 // The momx / momy move is bad, so try to slide 673 // along a wall. 674 // Find the first line hit, move flush to it, 675 // and slide along it 676 // 677 // This is a kludgy mess. 678 // 679 void P_SlideMove (mobj_t* mo) 680 { 681 fixed_t leadx; 682 fixed_t leady; 683 fixed_t trailx; 684 fixed_t traily; 685 fixed_t newx; 686 fixed_t newy; 687 int hitcount; 688 689 ::g->slidemo = mo; 690 hitcount = 0; 691 692 retry: 693 if (++hitcount == 3) 694 goto stairstep; // don't loop forever 695 696 697 // ::g->trace along the three leading corners 698 if (mo->momx > 0) 699 { 700 leadx = mo->x + mo->radius; 701 trailx = mo->x - mo->radius; 702 } 703 else 704 { 705 leadx = mo->x - mo->radius; 706 trailx = mo->x + mo->radius; 707 } 708 709 if (mo->momy > 0) 710 { 711 leady = mo->y + mo->radius; 712 traily = mo->y - mo->radius; 713 } 714 else 715 { 716 leady = mo->y - mo->radius; 717 traily = mo->y + mo->radius; 718 } 719 720 ::g->bestslidefrac = FRACUNIT+1; 721 722 P_PathTraverse ( leadx, leady, leadx+mo->momx, leady+mo->momy, 723 PT_ADDLINES, PTR_SlideTraverse ); 724 P_PathTraverse ( trailx, leady, trailx+mo->momx, leady+mo->momy, 725 PT_ADDLINES, PTR_SlideTraverse ); 726 P_PathTraverse ( leadx, traily, leadx+mo->momx, traily+mo->momy, 727 PT_ADDLINES, PTR_SlideTraverse ); 728 729 // move up to the wall 730 if (::g->bestslidefrac == FRACUNIT+1) 731 { 732 // the move most have hit the middle, so stairstep 733 stairstep: 734 if (!P_TryMove (mo, mo->x, mo->y + mo->momy)) 735 P_TryMove (mo, mo->x + mo->momx, mo->y); 736 return; 737 } 738 739 // fudge a bit to make sure it doesn't hit 740 ::g->bestslidefrac -= 0x800; 741 if (::g->bestslidefrac > 0) 742 { 743 newx = FixedMul (mo->momx, ::g->bestslidefrac); 744 newy = FixedMul (mo->momy, ::g->bestslidefrac); 745 746 if (!P_TryMove (mo, mo->x+newx, mo->y+newy)) 747 goto stairstep; 748 } 749 750 // Now continue along the wall. 751 // First calculate remainder. 752 ::g->bestslidefrac = FRACUNIT-(::g->bestslidefrac+0x800); 753 754 if (::g->bestslidefrac > FRACUNIT) 755 ::g->bestslidefrac = FRACUNIT; 756 757 if (::g->bestslidefrac <= 0) 758 return; 759 760 ::g->tmxmove = FixedMul (mo->momx, ::g->bestslidefrac); 761 ::g->tmymove = FixedMul (mo->momy, ::g->bestslidefrac); 762 763 P_HitSlideLine (::g->bestslideline); // clip the moves 764 765 mo->momx = ::g->tmxmove; 766 mo->momy = ::g->tmymove; 767 768 if (!P_TryMove (mo, mo->x+::g->tmxmove, mo->y+::g->tmymove)) 769 { 770 goto retry; 771 } 772 } 773 774 775 // 776 // P_LineAttack 777 // 778 779 // Height if not aiming up or down 780 // ???: use slope for monsters? 781 782 783 784 // slopes to top and bottom of target 785 786 787 // 788 // PTR_AimTraverse 789 // Sets linetaget and ::g->aimslope when a target is aimed at. 790 // 791 qboolean 792 PTR_AimTraverse (intercept_t* in) 793 { 794 line_t* li; 795 mobj_t* th; 796 fixed_t slope; 797 fixed_t thingtopslope; 798 fixed_t thingbottomslope; 799 fixed_t dist; 800 801 if (in->isaline) 802 { 803 li = in->d.line; 804 805 if ( !(li->flags & ML_TWOSIDED) ) 806 return false; // stop 807 808 // Crosses a two sided line. 809 // A two sided line will restrict 810 // the possible target ranges. 811 P_LineOpening (li); 812 813 if (::g->openbottom >= ::g->opentop) 814 return false; // stop 815 816 dist = FixedMul (::g->attackrange, in->frac); 817 818 if (li->frontsector->floorheight != li->backsector->floorheight) 819 { 820 slope = FixedDiv (::g->openbottom - ::g->shootz , dist); 821 if (slope > ::g->bottomslope) 822 ::g->bottomslope = slope; 823 } 824 825 if (li->frontsector->ceilingheight != li->backsector->ceilingheight) 826 { 827 slope = FixedDiv (::g->opentop - ::g->shootz , dist); 828 if (slope < ::g->topslope) 829 ::g->topslope = slope; 830 } 831 832 if (::g->topslope <= ::g->bottomslope) 833 return false; // stop 834 835 return true; // shot continues 836 } 837 838 // shoot a thing 839 th = in->d.thing; 840 if (th == ::g->shootthing) 841 return true; // can't shoot self 842 843 if (!(th->flags&MF_SHOOTABLE)) 844 return true; // corpse or something 845 846 // check angles to see if the thing can be aimed at 847 dist = FixedMul (::g->attackrange, in->frac); 848 thingtopslope = FixedDiv (th->z+th->height - ::g->shootz , dist); 849 850 if (thingtopslope < ::g->bottomslope) 851 return true; // shot over the thing 852 853 thingbottomslope = FixedDiv (th->z - ::g->shootz, dist); 854 855 if (thingbottomslope > ::g->topslope) 856 return true; // shot under the thing 857 858 // this thing can be hit! 859 if (thingtopslope > ::g->topslope) 860 thingtopslope = ::g->topslope; 861 862 if (thingbottomslope < ::g->bottomslope) 863 thingbottomslope = ::g->bottomslope; 864 865 ::g->aimslope = (thingtopslope+thingbottomslope)/2; 866 ::g->linetarget = th; 867 868 return false; // don't go any farther 869 } 870 871 872 // 873 // PTR_ShootTraverse 874 // 875 qboolean PTR_ShootTraverse (intercept_t* in) 876 { 877 fixed_t x; 878 fixed_t y; 879 fixed_t z; 880 fixed_t frac; 881 882 line_t* li; 883 884 mobj_t* th; 885 886 fixed_t slope; 887 fixed_t dist; 888 fixed_t thingtopslope; 889 fixed_t thingbottomslope; 890 891 if (in->isaline) 892 { 893 li = in->d.line; 894 895 if (li->special) 896 P_ShootSpecialLine (::g->shootthing, li); 897 898 if ( !(li->flags & ML_TWOSIDED) ) 899 goto hitline; 900 901 // crosses a two sided line 902 P_LineOpening (li); 903 904 dist = FixedMul (::g->attackrange, in->frac); 905 906 if (li->frontsector->floorheight != li->backsector->floorheight) 907 { 908 slope = FixedDiv (::g->openbottom - ::g->shootz , dist); 909 if (slope > ::g->aimslope) 910 goto hitline; 911 } 912 913 if (li->frontsector->ceilingheight != li->backsector->ceilingheight) 914 { 915 slope = FixedDiv (::g->opentop - ::g->shootz , dist); 916 if (slope < ::g->aimslope) 917 goto hitline; 918 } 919 920 // shot continues 921 return true; 922 923 924 // hit line 925 hitline: 926 // position a bit closer 927 frac = in->frac - FixedDiv (4*FRACUNIT,::g->attackrange); 928 x = ::g->trace.x + FixedMul (::g->trace.dx, frac); 929 y = ::g->trace.y + FixedMul (::g->trace.dy, frac); 930 z = ::g->shootz + FixedMul (::g->aimslope, FixedMul(frac, ::g->attackrange)); 931 932 if (li->frontsector->ceilingpic == ::g->skyflatnum) 933 { 934 // don't shoot the sky! 935 if (z > li->frontsector->ceilingheight) 936 return false; 937 938 // it's a sky hack wall 939 if (li->backsector && li->backsector->ceilingpic == ::g->skyflatnum) 940 return false; 941 } 942 943 mobj_t * sourceObject = ::g->shootthing; 944 if( sourceObject ) { 945 946 if( ( sourceObject->player) == &(::g->players[DoomLib::GetPlayer()]) ) { 947 948 // Fist Punch. 949 if( ::g->attackrange == MELEERANGE ) { 950 } 951 } 952 } 953 954 // Spawn bullet puffs. 955 P_SpawnPuff (x,y,z); 956 957 // don't go any farther 958 return false; 959 } 960 961 // shoot a thing 962 th = in->d.thing; 963 if (th == ::g->shootthing) 964 return true; // can't shoot self 965 966 if (!(th->flags&MF_SHOOTABLE)) 967 return true; // corpse or something 968 969 // check angles to see if the thing can be aimed at 970 dist = FixedMul (::g->attackrange, in->frac); 971 thingtopslope = FixedDiv (th->z+th->height - ::g->shootz , dist); 972 973 if (thingtopslope < ::g->aimslope) 974 return true; // shot over the thing 975 976 thingbottomslope = FixedDiv (th->z - ::g->shootz, dist); 977 978 if (thingbottomslope > ::g->aimslope) 979 return true; // shot under the thing 980 981 982 // hit thing 983 // position a bit closer 984 frac = in->frac - FixedDiv (10*FRACUNIT,::g->attackrange); 985 986 x = ::g->trace.x + FixedMul (::g->trace.dx, frac); 987 y = ::g->trace.y + FixedMul (::g->trace.dy, frac); 988 z = ::g->shootz + FixedMul (::g->aimslope, FixedMul(frac, ::g->attackrange)); 989 990 // check for friendly fire. 991 #ifdef ID_ENABLE_DOOM_CLASSIC_NETWORKING 992 if( th && gameLocal->GetMatchParms().GetGameType() != GAME_TYPE_PVP ) { 993 player_t * hitPlayer = th->player; 994 995 if( hitPlayer ) { 996 997 mobj_t * sourceObject = ::g->shootthing; 998 999 if( sourceObject ) { 1000 player_t* sourcePlayer = sourceObject->player; 1001 1002 if( sourcePlayer != NULL && sourcePlayer != hitPlayer && !gameLocal->GetMatchParms().AllowFriendlyFire() ) { 1003 return true; 1004 } 1005 } 1006 } 1007 } 1008 #endif 1009 1010 mobj_t * sourceObject = ::g->shootthing; 1011 if( sourceObject ) { 1012 1013 if( ( sourceObject->player) == &(::g->players[DoomLib::GetPlayer()]) ) { 1014 1015 // Fist Punch. 1016 if( ::g->attackrange == MELEERANGE ) { 1017 } 1018 } 1019 } 1020 1021 1022 // Spawn bullet puffs or blod spots, 1023 // depending on target type. 1024 if (in->d.thing->flags & MF_NOBLOOD) 1025 P_SpawnPuff (x,y,z); 1026 else 1027 P_SpawnBlood (x,y,z, ::g->la_damage); 1028 1029 if (::g->la_damage) 1030 P_DamageMobj (th, ::g->shootthing, ::g->shootthing, ::g->la_damage); 1031 1032 // don't go any farther 1033 return false; 1034 1035 } 1036 1037 1038 // 1039 // P_AimLineAttack 1040 // 1041 fixed_t 1042 P_AimLineAttack 1043 ( mobj_t* t1, 1044 angle_t angle, 1045 fixed_t distance ) 1046 { 1047 fixed_t x2; 1048 fixed_t y2; 1049 1050 angle >>= ANGLETOFINESHIFT; 1051 ::g->shootthing = t1; 1052 1053 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; 1054 y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; 1055 ::g->shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; 1056 1057 // can't shoot outside view angles 1058 ::g->topslope = 100*FRACUNIT/160; 1059 ::g->bottomslope = -100*FRACUNIT/160; 1060 1061 ::g->attackrange = distance; 1062 ::g->linetarget = NULL; 1063 1064 P_PathTraverse ( t1->x, t1->y, 1065 x2, y2, 1066 PT_ADDLINES|PT_ADDTHINGS, 1067 PTR_AimTraverse ); 1068 1069 if (::g->linetarget) 1070 return ::g->aimslope; 1071 1072 return 0; 1073 } 1074 1075 1076 // 1077 // P_LineAttack 1078 // If damage == 0, it is just a test ::g->trace 1079 // that will leave ::g->linetarget set. 1080 // 1081 void 1082 P_LineAttack 1083 ( mobj_t* t1, 1084 angle_t angle, 1085 fixed_t distance, 1086 fixed_t slope, 1087 int damage ) 1088 { 1089 fixed_t x2; 1090 fixed_t y2; 1091 1092 angle >>= ANGLETOFINESHIFT; 1093 ::g->shootthing = t1; 1094 ::g->la_damage = damage; 1095 x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; 1096 y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; 1097 ::g->shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; 1098 ::g->attackrange = distance; 1099 ::g->aimslope = slope; 1100 1101 P_PathTraverse ( t1->x, t1->y, 1102 x2, y2, 1103 PT_ADDLINES|PT_ADDTHINGS, 1104 PTR_ShootTraverse ); 1105 } 1106 1107 1108 1109 // 1110 // USE LINES 1111 // 1112 1113 qboolean PTR_UseTraverse (intercept_t* in) 1114 { 1115 int side; 1116 1117 if (!in->d.line->special) 1118 { 1119 P_LineOpening (in->d.line); 1120 if (::g->openrange <= 0) 1121 { 1122 S_StartSound (::g->usething, sfx_noway); 1123 1124 // can't use through a wall 1125 return false; 1126 } 1127 // not a special line, but keep checking 1128 return true ; 1129 } 1130 1131 side = 0; 1132 if (P_PointOnLineSide (::g->usething->x, ::g->usething->y, in->d.line) == 1) 1133 side = 1; 1134 1135 // return false; // don't use back side 1136 1137 P_UseSpecialLine (::g->usething, in->d.line, side); 1138 1139 // can't use for than one special line in a row 1140 return false; 1141 } 1142 1143 1144 // 1145 // P_UseLines 1146 // Looks for special ::g->lines in front of the player to activate. 1147 // 1148 void P_UseLines (player_t* player) 1149 { 1150 int angle; 1151 fixed_t x1; 1152 fixed_t y1; 1153 fixed_t x2; 1154 fixed_t y2; 1155 1156 ::g->usething = player->mo; 1157 1158 angle = player->mo->angle >> ANGLETOFINESHIFT; 1159 1160 x1 = player->mo->x; 1161 y1 = player->mo->y; 1162 x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; 1163 y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; 1164 1165 P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); 1166 } 1167 1168 1169 // 1170 // RADIUS ATTACK 1171 // 1172 1173 1174 // 1175 // PIT_RadiusAttack 1176 // "bombsource" is the creature 1177 // that caused the explosion at "bombspot". 1178 // 1179 qboolean PIT_RadiusAttack (mobj_t* thing) 1180 { 1181 fixed_t dx; 1182 fixed_t dy; 1183 fixed_t dist; 1184 1185 if (!(thing->flags & MF_SHOOTABLE) ) 1186 return true; 1187 1188 // Boss spider and cyborg 1189 // take no damage from concussion. 1190 if (thing->type == MT_CYBORG 1191 || thing->type == MT_SPIDER) 1192 return true; 1193 1194 dx = abs(thing->x - ::g->bombspot->x); 1195 dy = abs(thing->y - ::g->bombspot->y); 1196 1197 dist = dx>dy ? dx : dy; 1198 dist = (dist - thing->radius) >> FRACBITS; 1199 1200 if (dist < 0) 1201 dist = 0; 1202 1203 if (dist >= ::g->bombdamage) 1204 return true; // out of range 1205 1206 if ( P_CheckSight (thing, ::g->bombspot) ) 1207 { 1208 // must be in direct path 1209 P_DamageMobj (thing, ::g->bombspot, ::g->bombsource, ::g->bombdamage - dist); 1210 } 1211 1212 return true; 1213 } 1214 1215 1216 // 1217 // P_RadiusAttack 1218 // Source is the creature that caused the explosion at spot. 1219 // 1220 void 1221 P_RadiusAttack 1222 ( mobj_t* spot, 1223 mobj_t* source, 1224 int damage ) 1225 { 1226 int x; 1227 int y; 1228 1229 int xl; 1230 int xh; 1231 int yl; 1232 int yh; 1233 1234 fixed_t dist; 1235 1236 dist = (damage+MAXRADIUS)<<FRACBITS; 1237 yh = (spot->y + dist - ::g->bmaporgy)>>MAPBLOCKSHIFT; 1238 yl = (spot->y - dist - ::g->bmaporgy)>>MAPBLOCKSHIFT; 1239 xh = (spot->x + dist - ::g->bmaporgx)>>MAPBLOCKSHIFT; 1240 xl = (spot->x - dist - ::g->bmaporgx)>>MAPBLOCKSHIFT; 1241 ::g->bombspot = spot; 1242 ::g->bombsource = source; 1243 ::g->bombdamage = damage; 1244 1245 for (y=yl ; y<=yh ; y++) 1246 for (x=xl ; x<=xh ; x++) 1247 P_BlockThingsIterator (x, y, PIT_RadiusAttack ); 1248 } 1249 1250 1251 1252 // 1253 // SECTOR HEIGHT CHANGING 1254 // After modifying a ::g->sectors floor or ceiling height, 1255 // call this routine to adjust the positions 1256 // of all things that touch the sector. 1257 // 1258 // If anything doesn't fit anymore, true will be returned. 1259 // If crunch is true, they will take damage 1260 // as they are being crushed. 1261 // If Crunch is false, you should set the sector height back 1262 // the way it was and call P_ChangeSector again 1263 // to undo the changes. 1264 // 1265 1266 1267 // 1268 // PIT_ChangeSector 1269 // 1270 qboolean PIT_ChangeSector (mobj_t* thing) 1271 { 1272 mobj_t* mo; 1273 1274 if (P_ThingHeightClip (thing)) 1275 { 1276 // keep checking 1277 return true; 1278 } 1279 1280 1281 // crunch bodies to giblets 1282 if (thing->health <= 0) 1283 { 1284 P_SetMobjState (thing, S_GIBS); 1285 1286 thing->flags &= ~MF_SOLID; 1287 thing->height = 0; 1288 thing->radius = 0; 1289 1290 // keep checking 1291 return true; 1292 } 1293 1294 // crunch dropped items 1295 if (thing->flags & MF_DROPPED) 1296 { 1297 P_RemoveMobj (thing); 1298 1299 // keep checking 1300 return true; 1301 } 1302 1303 if (! (thing->flags & MF_SHOOTABLE) ) 1304 { 1305 // assume it is bloody gibs or something 1306 return true; 1307 } 1308 1309 ::g->nofit = true; 1310 1311 if (::g->crushchange && !(::g->leveltime&3) ) 1312 { 1313 P_DamageMobj(thing,NULL,NULL,10); 1314 1315 // spray blood in a random direction 1316 mo = P_SpawnMobj (thing->x, 1317 thing->y, 1318 thing->z + thing->height/2, MT_BLOOD); 1319 1320 mo->momx = (P_Random() - P_Random ())<<12; 1321 mo->momy = (P_Random() - P_Random ())<<12; 1322 } 1323 1324 // keep checking (crush other things) 1325 return true; 1326 } 1327 1328 1329 1330 // 1331 // P_ChangeSector 1332 // 1333 qboolean 1334 P_ChangeSector 1335 ( sector_t* sector, 1336 qboolean crunch ) 1337 { 1338 int x; 1339 int y; 1340 1341 ::g->nofit = false; 1342 ::g->crushchange = crunch; 1343 1344 // re-check heights for all things near the moving sector 1345 for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) 1346 for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) 1347 P_BlockThingsIterator (x, y, PIT_ChangeSector); 1348 1349 1350 return ::g->nofit; 1351 } 1352 1353