A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 2624 lines 72 kB view raw
1/* Emacs style mode select -*- C++ -*- 2 *----------------------------------------------------------------------------- 3 * 4 * 5 * PrBoom a Doom port merged with LxDoom and LSDLDoom 6 * based on BOOM, a modified and improved DOOM engine 7 * Copyright (C) 1999 by 8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman 9 * Copyright (C) 1999-2000 by 10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 25 * 02111-1307, USA. 26 * 27 * DESCRIPTION: 28 * Enemy thinking, AI. 29 * Action Pointer Functions 30 * that are associated with states/frames. 31 * 32 *-----------------------------------------------------------------------------*/ 33 34#include "doomstat.h" 35#include "m_random.h" 36#include "r_main.h" 37#include "p_maputl.h" 38#include "p_map.h" 39#include "p_setup.h" 40#include "p_spec.h" 41#include "s_sound.h" 42#include "sounds.h" 43#include "p_inter.h" 44#include "g_game.h" 45#include "p_enemy.h" 46#include "p_tick.h" 47#include "m_bbox.h" 48 49#include "rockmacros.h" 50 51static mobj_t *current_actor; 52 53enum { 54 DI_EAST, 55 DI_NORTHEAST, 56 DI_NORTH, 57 DI_NORTHWEST, 58 DI_WEST, 59 DI_SOUTHWEST, 60 DI_SOUTH, 61 DI_SOUTHEAST, 62 DI_NODIR, 63 NUMDIRS 64}; 65typedef unsigned dirtype_t; 66 67void A_Fall(mobj_t *actor); 68void A_FaceTarget(mobj_t *actor); 69static void P_NewChaseDir(mobj_t *actor); 70void P_ZBumpCheck(mobj_t *); // phares 71 72// 73// ENEMY THINKING 74// Enemies are allways spawned 75// with targetplayer = -1, threshold = 0 76// Most monsters are spawned unaware of all players, 77// but some can be made preaware 78// 79 80// 81// Called by P_NoiseAlert. 82// Recursively traverse adjacent sectors, 83// sound blocking lines cut off traversal. 84// 85// killough 5/5/98: reformatted, cleaned up 86 87static void P_RecursiveSound(sector_t *sec, int soundblocks, 88 mobj_t *soundtarget) 89{ 90 int i; 91 92 // wake up all monsters in this sector 93 if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) 94 return; // already flooded 95 96 sec->validcount = validcount; 97 sec->soundtraversed = soundblocks+1; 98 P_SetTarget(&sec->soundtarget, soundtarget); 99 100 for (i=0; i<sec->linecount; i++) 101 { 102 sector_t *other; 103 line_t *check = sec->lines[i]; 104 105 if (!(check->flags & ML_TWOSIDED)) 106 continue; 107 108 P_LineOpening(check); 109 110 if (openrange <= 0) 111 continue; // closed door 112 113 other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector; 114 115 if (!(check->flags & ML_SOUNDBLOCK)) 116 P_RecursiveSound(other, soundblocks, soundtarget); 117 else 118 if (!soundblocks) 119 P_RecursiveSound(other, 1, soundtarget); 120 } 121} 122 123// 124// P_NoiseAlert 125// If a monster yells at a player, 126// it will alert other monsters to the player. 127// 128void P_NoiseAlert(mobj_t *target, mobj_t *emitter) 129{ 130 validcount++; 131 P_RecursiveSound(emitter->subsector->sector, 0, target); 132} 133 134// 135// P_CheckMeleeRange 136// 137 138static boolean P_CheckMeleeRange(mobj_t *actor) 139{ 140 mobj_t *pl = actor->target; 141 142 return // killough 7/18/98: friendly monsters don't attack other friends 143 pl && !(actor->flags & pl->flags & MF_FRIEND) && 144 (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) < 145 MELEERANGE - 20*FRACUNIT + pl->info->radius) && 146 P_CheckSight(actor, actor->target); 147} 148 149// 150// P_HitFriend() 151// 152// killough 12/98 153// This function tries to prevent shooting at friends 154 155static boolean P_HitFriend(mobj_t *actor) 156{ 157 return actor->flags & MF_FRIEND && actor->target && 158 (P_AimLineAttack(actor, 159 R_PointToAngle2(actor->x, actor->y, 160 actor->target->x, actor->target->y), 161 P_AproxDistance(actor->x-actor->target->x, 162 actor->y-actor->target->y), 0), 163 linetarget) && linetarget != actor->target && 164 !((linetarget->flags ^ actor->flags) & MF_FRIEND); 165} 166 167// 168// P_CheckMissileRange 169// 170boolean P_CheckMissileRange(mobj_t *actor) 171{ 172 fixed_t dist; 173 174 if (!P_CheckSight(actor, actor->target)) 175 return false; 176 177 if (actor->flags & MF_JUSTHIT) 178 { // the target just hit the enemy, so fight back! 179 actor->flags &= ~MF_JUSTHIT; 180 181 /* killough 7/18/98: no friendly fire at corpses 182 * killough 11/98: prevent too much infighting among friends 183 * cph - yikes, talk about fitting everything on one line... */ 184 185 return 186 !(actor->flags & MF_FRIEND) || 187 (actor->target->health > 0 && 188 (!(actor->target->flags & MF_FRIEND) || 189 (actor->target->player ? 190 monster_infighting || P_Random(pr_defect) >128 : 191 !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128))); 192 } 193 194 /* killough 7/18/98: friendly monsters don't attack other friendly 195 * monsters or players (except when attacked, and then only once) 196 */ 197 if (actor->flags & actor->target->flags & MF_FRIEND) 198 return false; 199 200 if (actor->reactiontime) 201 return false; // do not attack yet 202 203 // OPTIMIZE: get this from a global checksight 204 dist = P_AproxDistance ( actor->x-actor->target->x, 205 actor->y-actor->target->y) - 64*FRACUNIT; 206 207 if (!actor->info->meleestate) 208 dist -= 128*FRACUNIT; // no melee attack, so fire more 209 210 dist >>= FRACBITS; 211 212 if (actor->type == MT_VILE) 213 if (dist > 14*64) 214 return false; // too far away 215 216 217 if (actor->type == MT_UNDEAD) 218 { 219 if (dist < 196) 220 return false; // close for fist attack 221 dist >>= 1; 222 } 223 224 if (actor->type == MT_CYBORG || 225 actor->type == MT_SPIDER || 226 actor->type == MT_SKULL) 227 dist >>= 1; 228 229 if (dist > 200) 230 dist = 200; 231 232 if (actor->type == MT_CYBORG && dist > 160) 233 dist = 160; 234 235 if (P_Random(pr_missrange) < dist) 236 return false; 237 238 if (P_HitFriend(actor)) 239 return false; 240 241 return true; 242} 243 244/* 245 * P_IsOnLift 246 * 247 * killough 9/9/98: 248 * 249 * Returns true if the object is on a lift. Used for AI, 250 * since it may indicate the need for crowded conditions, 251 * or that a monster should stay on the lift for a while 252 * while it goes up or down. 253 */ 254 255static boolean P_IsOnLift(const mobj_t *actor) 256{ 257 const sector_t *sec = actor->subsector->sector; 258 line_t line; 259 int l; 260 261 // Short-circuit: it's on a lift which is active. 262 if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise) 263 return true; 264 265 // Check to see if it's in a sector which can be activated as a lift. 266 if ((line.tag = sec->tag)) 267 for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;) 268 switch (lines[l].special) 269 { 270case 10: case 14: case 15: case 20: case 21: case 22: 271case 47: case 53: case 62: case 66: case 67: case 68: 272case 87: case 88: case 95: case 120: case 121: case 122: 273case 123: case 143: case 162: case 163: case 181: case 182: 274case 144: case 148: case 149: case 211: case 227: case 228: 275case 231: case 232: case 235: case 236: 276 return true; 277 } 278 279 return false; 280} 281 282/* 283 * P_IsUnderDamage 284 * 285 * killough 9/9/98: 286 * 287 * Returns nonzero if the object is under damage based on 288 * their current position. Returns 1 if the damage is moderate, 289 * -1 if it is serious. Used for AI. 290 */ 291 292static int P_IsUnderDamage(mobj_t *actor) 293{ 294 const struct msecnode_s *seclist; 295 const ceiling_t *cl; // Crushing ceiling 296 int dir = 0; 297 for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext) 298 if ((cl = seclist->m_sector->ceilingdata) && 299 cl->thinker.function == T_MoveCeiling) 300 dir |= cl->direction; 301 return dir; 302} 303 304// 305// P_Move 306// Move in the current direction, 307// returns false if the move is blocked. 308// 309 310static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; 311static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; 312 313// 1/11/98 killough: Limit removed on special lines crossed 314extern line_t **spechit; // New code -- killough 315extern int numspechit; 316 317static boolean P_Move(mobj_t *actor, boolean dropoff) /* killough 9/12/98 */ 318{ 319 fixed_t tryx, tryy, deltax, deltay, origx, origy; 320 boolean try_ok; 321 int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98 322 int friction = ORIG_FRICTION; 323 int speed; 324 325 if (actor->movedir == DI_NODIR) 326 return false; 327 328#ifdef RANGECHECK 329 if ((unsigned)actor->movedir >= 8) 330 I_Error ("P_Move: Weird actor->movedir!"); 331#endif 332 333 // killough 10/98: make monsters get affected by ice and sludge too: 334 335 if (monster_friction) 336 movefactor = P_GetMoveFactor(actor, &friction); 337 338 speed = actor->info->speed; 339 340 if (friction < ORIG_FRICTION && // sludge 341 !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2) 342 * speed) / ORIG_FRICTION_FACTOR)) 343 speed = 1; // always give the monster a little bit of speed 344 345 tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]); 346 tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]); 347 348 try_ok = P_TryMove(actor, tryx, tryy, dropoff); 349 350 // killough 10/98: 351 // Let normal momentum carry them, instead of steptoeing them across ice. 352 353 if (try_ok && friction > ORIG_FRICTION) 354 { 355 actor->x = origx; 356 actor->y = origy; 357 movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4; 358 actor->momx += FixedMul(deltax, movefactor); 359 actor->momy += FixedMul(deltay, movefactor); 360 } 361 362 if (!try_ok) 363 { // open any specials 364 int good; 365 366 if (actor->flags & MF_FLOAT && floatok) 367 { 368 if (actor->z < tmfloorz) // must adjust height 369 actor->z += FLOATSPEED; 370 else 371 actor->z -= FLOATSPEED; 372 373 actor->flags |= MF_INFLOAT; 374 375 return true; 376 } 377 378 if (!numspechit) 379 return false; 380 381 actor->movedir = DI_NODIR; 382 383 /* if the special is not a door that can be opened, return false 384 * 385 * killough 8/9/98: this is what caused monsters to get stuck in 386 * doortracks, because it thought that the monster freed itself 387 * by opening a door, even if it was moving towards the doortrack, 388 * and not the door itself. 389 * 390 * killough 9/9/98: If a line blocking the monster is activated, 391 * return true 90% of the time. If a line blocking the monster is 392 * not activated, but some other line is, return false 90% of the 393 * time. A bit of randomness is needed to ensure it's free from 394 * lockups, but for most cases, it returns the correct result. 395 * 396 * Do NOT simply return false 1/4th of the time (causes monsters to 397 * back out when they shouldn't, and creates secondary stickiness). 398 */ 399 400 for (good = false; numspechit--; ) 401 if (P_UseSpecialLine(actor, spechit[numspechit], 0)) 402 good |= spechit[numspechit] == blockline ? 1 : 2; 403 404 /* cph - compatibility maze here 405 * Boom v2.01 and orig. Doom return "good" 406 * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3) 407 * MBF plays even more games 408 */ 409 if (!good || comp[comp_doorstuck]) return good; 410 if (!mbf_features) 411 return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */ 412 else /* finally, MBF code */ 413 return ((P_Random(pr_opendoor) >= 230) ^ (good & 1)); 414 } 415 else 416 actor->flags &= ~MF_INFLOAT; 417 418 /* killough 11/98: fall more slowly, under gravity, if felldown==true */ 419 if (!(actor->flags & MF_FLOAT) && 420 (!felldown || !mbf_features)) 421 actor->z = actor->floorz; 422 423 return true; 424} 425 426/* 427 * P_SmartMove 428 * 429 * killough 9/12/98: Same as P_Move, except smarter 430 */ 431 432static boolean P_SmartMove(mobj_t *actor) 433{ 434 mobj_t *target = actor->target; 435 int on_lift, dropoff = false, under_damage; 436 437 /* killough 9/12/98: Stay on a lift if target is on one */ 438 on_lift = !comp[comp_staylift] 439 && target && target->health > 0 440 && target->subsector->sector->tag==actor->subsector->sector->tag && 441 P_IsOnLift(actor); 442 443 under_damage = monster_avoid_hazards && P_IsUnderDamage(actor); 444 445 // killough 10/98: allow dogs to drop off of taller ledges sometimes. 446 // dropoff==1 means always allow it, dropoff==2 means only up to 128 high, 447 // and only if the target is immediately on the other side of the line. 448 449#ifdef DOGS 450 if (actor->type == MT_DOGS && target && dog_jumping && 451 !((target->flags ^ actor->flags) & MF_FRIEND) && 452 P_AproxDistance(actor->x - target->x, 453 actor->y - target->y) < FRACUNIT*144 && 454 P_Random(pr_dropoff) < 235) 455 dropoff = 2; 456#endif 457 458 if (!P_Move(actor, dropoff)) 459 return false; 460 461 // killough 9/9/98: avoid crushing ceilings or other damaging areas 462 if ( 463 (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift 464 !P_IsOnLift(actor)) 465 || 466 (monster_avoid_hazards && !under_damage && // Get away from damage 467 (under_damage = P_IsUnderDamage(actor)) && 468 (under_damage < 0 || P_Random(pr_avoidcrush) < 200)) 469 ) 470 actor->movedir = DI_NODIR; // avoid the area (most of the time anyway) 471 472 return true; 473} 474 475// 476// TryWalk 477// Attempts to move actor on 478// in its current (ob->moveangle) direction. 479// If blocked by either a wall or an actor 480// returns FALSE 481// If move is either clear or blocked only by a door, 482// returns TRUE and sets... 483// If a door is in the way, 484// an OpenDoor call is made to start it opening. 485// 486 487boolean P_TryWalk(mobj_t *actor) 488{ 489 if (!P_SmartMove(actor)) 490 return false; 491 actor->movecount = P_Random(pr_trywalk)&15; 492 return true; 493} 494 495// 496// P_DoNewChaseDir 497// 498// killough 9/8/98: 499// 500// Most of P_NewChaseDir(), except for what 501// determines the new direction to take 502// 503 504static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay) 505{ 506 dirtype_t xdir, ydir, tdir; 507 dirtype_t olddir = actor->movedir; 508 dirtype_t turnaround = olddir; 509 510 if (turnaround != DI_NODIR) // find reverse direction 511 turnaround ^= 4; 512 513 xdir = 514 deltax > 10*FRACUNIT ? DI_EAST : 515 deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR; 516 517 ydir = 518 deltay < -10*FRACUNIT ? DI_SOUTH : 519 deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR; 520 521 // try direct route 522 if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround != 523 (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST : 524 deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor)) 525 return; 526 527 // try other directions 528 if (P_Random(pr_newchase) > 200 || abs(deltay)>abs(deltax)) 529 tdir = xdir, xdir = ydir, ydir = tdir; 530 531 if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR && 532 (actor->movedir = xdir, P_TryWalk(actor))) 533 return; // either moved forward or attacked 534 535 if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR && 536 (actor->movedir = ydir, P_TryWalk(actor))) 537 return; 538 539 // there is no direct path to the player, so pick another direction. 540 if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor))) 541 return; 542 543 // randomly determine direction of search 544 if (P_Random(pr_newchasedir) & 1) 545 { 546 for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) 547 if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) 548 return; 549 } 550 else 551 for (tdir = DI_SOUTHEAST; tdir != (dirtype_t)(DI_EAST-1); tdir--) 552 if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) 553 return; 554 555 if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor)) 556 actor->movedir = DI_NODIR; 557} 558 559// 560// killough 11/98: 561// 562// Monsters try to move away from tall dropoffs. 563// 564// In Doom, they were never allowed to hang over dropoffs, 565// and would remain stuck if involuntarily forced over one. 566// This logic, combined with p_map.c (P_TryMove), allows 567// monsters to free themselves without making them tend to 568// hang over dropoffs. 569 570static fixed_t dropoff_deltax, dropoff_deltay, floorz; 571 572static boolean PIT_AvoidDropoff(line_t *line) 573{ 574 if (line->backsector && // Ignore one-sided linedefs 575 tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] && 576 tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] && 577 tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted 578 tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] && 579 P_BoxOnLineSide(tmbbox, line) == -1) 580 { 581 fixed_t front = line->frontsector->floorheight; 582 fixed_t back = line->backsector->floorheight; 583 angle_t angle; 584 585 // The monster must contact one of the two floors, 586 // and the other must be a tall dropoff (more than 24). 587 588 if (back == floorz && front < floorz - FRACUNIT*24) 589 angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff 590 else 591 if (front == floorz && back < floorz - FRACUNIT*24) 592 angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff 593 else 594 return true; 595 596 // Move away from dropoff at a standard speed. 597 // Multiple contacted linedefs are cumulative (e.g. hanging over corner) 598 dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32; 599 dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32; 600 } 601 return true; 602} 603 604// 605// Driver for above 606// 607 608static fixed_t P_AvoidDropoff(mobj_t *actor) 609{ 610 int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; 611 int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; 612 int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; 613 int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; 614 int bx, by; 615 616 floorz = actor->z; // remember floor height 617 618 dropoff_deltax = dropoff_deltay = 0; 619 620 // check lines 621 622 validcount++; 623 for (bx=xl ; bx<=xh ; bx++) 624 for (by=yl ; by<=yh ; by++) 625 P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines 626 627 return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed 628} 629 630// 631// P_NewChaseDir 632// 633// killough 9/8/98: Split into two functions 634// 635 636static void P_NewChaseDir(mobj_t *actor) 637{ 638 mobj_t *target = actor->target; 639 fixed_t deltax = target->x - actor->x; 640 fixed_t deltay = target->y - actor->y; 641 642 // killough 8/8/98: sometimes move away from target, keeping distance 643 // 644 // 1) Stay a certain distance away from a friend, to avoid being in their way 645 // 2) Take advantage over an enemy without missiles, by keeping distance 646 647 actor->strafecount = 0; 648 649 if (mbf_features) { 650 if (actor->floorz - actor->dropoffz > FRACUNIT*24 && 651 actor->z <= actor->floorz && 652 !(actor->flags & (MF_DROPOFF|MF_FLOAT)) && 653 !comp[comp_dropoff] && 654 P_AvoidDropoff(actor)) /* Move away from dropoff */ 655 { 656 P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay); 657 658 // If moving away from dropoff, set movecount to 1 so that 659 // small steps are taken to get monster away from dropoff. 660 661 actor->movecount = 1; 662 return; 663 } 664 else 665 { 666 fixed_t dist = P_AproxDistance(deltax, deltay); 667 668 // Move away from friends when too close, except 669 // in certain situations (e.g. a crowded lift) 670 671 if (actor->flags & target->flags & MF_FRIEND && 672 distfriend << FRACBITS > dist && 673 !P_IsOnLift(target) && !P_IsUnderDamage(actor)) 674 { 675 deltax = -deltax, deltay = -deltay; 676 } else 677 if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND) 678 { // Live enemy target 679 if (monster_backing && 680 actor->info->missilestate && actor->type != MT_SKULL && 681 ((!target->info->missilestate && dist < MELEERANGE*2) || 682 (target->player && dist < MELEERANGE*3 && 683 (target->player->readyweapon == wp_fist || 684 target->player->readyweapon == wp_chainsaw)))) 685 { // Back away from melee attacker 686 actor->strafecount = P_Random(pr_enemystrafe) & 15; 687 deltax = -deltax, deltay = -deltay; 688 } 689 } 690 } 691 } 692 693 P_DoNewChaseDir(actor, deltax, deltay); 694 695 // If strafing, set movecount to strafecount so that old Doom 696 // logic still works the same, except in the strafing part 697 698 if (actor->strafecount) 699 actor->movecount = actor->strafecount; 700} 701 702// 703// P_IsVisible 704// 705// killough 9/9/98: whether a target is visible to a monster 706// 707 708static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround) 709{ 710 if (!allaround) 711 { 712 angle_t an = R_PointToAngle2(actor->x, actor->y, 713 mo->x, mo->y) - actor->angle; 714 if (an > ANG90 && an < ANG270 && 715 P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE) 716 return false; 717 } 718 return P_CheckSight(actor, mo); 719} 720 721// 722// PIT_FindTarget 723// 724// killough 9/5/98 725// 726// Finds monster targets for other monsters 727// 728 729static int current_allaround; 730 731static boolean PIT_FindTarget(mobj_t *mo) 732{ 733 mobj_t *actor = current_actor; 734 735 if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target 736 mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL))) 737 return true; 738 739 // If the monster is already engaged in a one-on-one attack 740 // with a healthy friend, don't attack around 60% the time 741 { 742 const mobj_t *targ = mo->target; 743 if (targ && targ->target == mo && 744 P_Random(pr_skiptarget) > 100 && 745 (targ->flags ^ mo->flags) & MF_FRIEND && 746 targ->health*2 >= targ->info->spawnhealth) 747 return true; 748 } 749 750 if (!P_IsVisible(actor, mo, current_allaround)) 751 return true; 752 753 P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target 754 P_SetTarget(&actor->target, mo); // Found target 755 756 // Move the selected monster to the end of its associated 757 // list, so that it gets searched last next time. 758 759 { 760 thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ? 761 th_friends : th_enemies]; 762 (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev; 763 (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker; 764 (mo->thinker.cnext = cap)->cprev = &mo->thinker; 765 } 766 767 return false; 768} 769 770// 771// P_LookForPlayers 772// If allaround is false, only look 180 degrees in front. 773// Returns true if a player is targeted. 774// 775 776static boolean P_LookForPlayers(mobj_t *actor, boolean allaround) 777{ 778 player_t *player; 779 int stop, stopc, c; 780 781 if (actor->flags & MF_FRIEND) 782 { // killough 9/9/98: friendly monsters go about players differently 783 int anyone; 784 785#if 0 786 if (!allaround) // If you want friendly monsters not to awaken unprovoked 787 return false; 788#endif 789 790 // Go back to a player, no matter whether it's visible or not 791 for (anyone=0; anyone<=1; anyone++) 792 for (c=0; c<MAXPLAYERS; c++) 793 if (playeringame[c] && players[c].playerstate==PST_LIVE && 794 (anyone || P_IsVisible(actor, players[c].mo, allaround))) 795 { 796 P_SetTarget(&actor->target, players[c].mo); 797 798 // killough 12/98: 799 // get out of refiring loop, to avoid hitting player accidentally 800 801 if (actor->info->missilestate) 802 { 803 P_SetMobjState(actor, actor->info->seestate); 804 actor->flags &= ~MF_JUSTHIT; 805 } 806 807 return true; 808 } 809 810 return false; 811 } 812 813 // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98: 814 stop = (actor->lastlook-1)&(MAXPLAYERS-1); 815 816 c = 0; 817 818 stopc = !mbf_features && 819 !demo_compatibility && monsters_remember ? 820 MAXPLAYERS : 2; // killough 9/9/98 821 822 for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1)) 823 { 824 if (!playeringame[actor->lastlook]) 825 continue; 826 827 // killough 2/15/98, 9/9/98: 828 if (c++ == stopc || actor->lastlook == stop) // done looking 829 return false; 830 831 player = &players[actor->lastlook]; 832 833 if (player->health <= 0) 834 continue; // dead 835 836 if (!P_IsVisible(actor, player->mo, allaround)) 837 continue; 838 839 P_SetTarget(&actor->target, player->mo); 840 841 /* killough 9/9/98: give monsters a threshold towards getting players 842 * (we don't want it to be too easy for a player with dogs :) 843 */ 844 if (!comp[comp_pursuit]) 845 actor->threshold = 60; 846 847 return true; 848 } 849} 850 851// 852// Friendly monsters, by Lee Killough 7/18/98 853// 854// Friendly monsters go after other monsters first, but 855// also return to owner if they cannot find any targets. 856// A marine's best friend :) killough 7/18/98, 9/98 857// 858 859static boolean P_LookForMonsters(mobj_t *actor, boolean allaround) 860{ 861 thinker_t *cap, *th; 862 863 if (demo_compatibility) 864 return false; 865 866 if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember && 867 !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends 868 { 869 P_SetTarget(&actor->target, actor->lastenemy); 870 P_SetTarget(&actor->lastenemy, NULL); 871 return true; 872 } 873 874 /* Old demos do not support monster-seeking bots */ 875 if (!mbf_features) 876 return false; 877 878 // Search the threaded list corresponding to this object's potential targets 879 cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends]; 880 881 // Search for new enemy 882 883 if (cap->cnext != cap) // Empty list? bail out early 884 { 885 int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT; 886 int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT; 887 int d; 888 889 current_actor = actor; 890 current_allaround = allaround; 891 892 // Search first in the immediate vicinity. 893 894 if (!P_BlockThingsIterator(x, y, PIT_FindTarget)) 895 return true; 896 897 for (d=1; d<5; d++) 898 { 899 int i = 1 - d; 900 do 901 if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) || 902 !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget)) 903 return true; 904 while (++i < d); 905 do 906 if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) || 907 !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget)) 908 return true; 909 while (--i + d >= 0); 910 } 911 912 { // Random number of monsters, to prevent patterns from forming 913 int n = (P_Random(pr_friends) & 31) + 15; 914 915 for (th = cap->cnext; th != cap; th = th->cnext) 916 if (--n < 0) 917 { 918 // Only a subset of the monsters were searched. Move all of 919 // the ones which were searched so far, to the end of the list. 920 921 (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext; 922 (cap->cprev = th->cprev)->cnext = cap; 923 (th->cprev = cap)->cnext = th; 924 break; 925 } 926 else 927 if (!PIT_FindTarget((mobj_t *) th)) // If target sighted 928 return true; 929 } 930 } 931 932 return false; // No monster found 933} 934 935// 936// P_LookForTargets 937// 938// killough 9/5/98: look for targets to go after, depending on kind of monster 939// 940 941static boolean P_LookForTargets(mobj_t *actor, int allaround) 942{ 943 return actor->flags & MF_FRIEND ? 944 P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround): 945 P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround); 946} 947 948// 949// P_HelpFriend 950// 951// killough 9/8/98: Help friends in danger of dying 952// 953 954static boolean P_HelpFriend(mobj_t *actor) 955{ 956 thinker_t *cap, *th; 957 958 // If less than 33% health, self-preservation rules 959 if (actor->health*3 < actor->info->spawnhealth) 960 return false; 961 962 current_actor = actor; 963 current_allaround = true; 964 965 // Possibly help a friend under 50% health 966 cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies]; 967 968 for (th = cap->cnext; th != cap; th = th->cnext) 969 if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth) 970 { 971 if (P_Random(pr_helpfriend) < 180) 972 break; 973 } 974 else 975 if (((mobj_t *) th)->flags & MF_JUSTHIT && 976 ((mobj_t *) th)->target && 977 ((mobj_t *) th)->target != actor->target && 978 !PIT_FindTarget(((mobj_t *) th)->target)) 979 { 980 // Ignore any attacking monsters, while searching for friend 981 actor->threshold = BASETHRESHOLD; 982 return true; 983 } 984 985 return false; 986} 987 988// 989// A_KeenDie 990// DOOM II special, map 32. 991// Uses special tag 666. 992// 993void A_KeenDie(mobj_t* mo) 994{ 995 thinker_t *th; 996 line_t junk; 997 998 A_Fall(mo); 999 1000 // scan the remaining thinkers to see if all Keens are dead 1001 1002 for (th = thinkercap.next ; th != &thinkercap ; th=th->next) 1003 if (th->function == P_MobjThinker) 1004 { 1005 mobj_t *mo2 = (mobj_t *) th; 1006 if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) 1007 return; // other Keen not dead 1008 } 1009 1010 junk.tag = 666; 1011 EV_DoDoor(&junk,p_open); 1012} 1013 1014 1015// 1016// ACTION ROUTINES 1017// 1018 1019// 1020// A_Look 1021// Stay in state until a player is sighted. 1022// 1023 1024void A_Look(mobj_t *actor) 1025{ 1026 mobj_t *targ = actor->subsector->sector->soundtarget; 1027 actor->threshold = 0; // any shot will wake up 1028 1029 /* killough 7/18/98: 1030 * Friendly monsters go after other monsters first, but 1031 * also return to player, without attacking them, if they 1032 * cannot find any targets. A marine's best friend :) 1033 */ 1034 actor->pursuecount = 0; 1035 1036 if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) && 1037 !((targ = actor->subsector->sector->soundtarget) && 1038 targ->flags & MF_SHOOTABLE && 1039 (P_SetTarget(&actor->target, targ), 1040 !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) && 1041 (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false))) 1042 return; 1043 1044 // go into chase state 1045 1046 if (actor->info->seesound) 1047 { 1048 int sound; 1049 switch (actor->info->seesound) 1050 { 1051 case sfx_posit1: 1052 case sfx_posit2: 1053 case sfx_posit3: 1054 sound = sfx_posit1+P_Random(pr_see)%3; 1055 break; 1056 1057 case sfx_bgsit1: 1058 case sfx_bgsit2: 1059 sound = sfx_bgsit1+P_Random(pr_see)%2; 1060 break; 1061 1062 default: 1063 sound = actor->info->seesound; 1064 break; 1065 } 1066 if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) 1067 S_StartSound(NULL, sound); // full volume 1068 else 1069 S_StartSound(actor, sound); 1070 } 1071 P_SetMobjState(actor, actor->info->seestate); 1072} 1073 1074// 1075// A_KeepChasing 1076// 1077// killough 10/98: 1078// Allows monsters to continue movement while attacking 1079// 1080 1081void A_KeepChasing(mobj_t *actor) 1082{ 1083 if (actor->movecount) 1084 { 1085 actor->movecount--; 1086 if (actor->strafecount) 1087 actor->strafecount--; 1088 P_SmartMove(actor); 1089 } 1090} 1091 1092// 1093// A_Chase 1094// Actor has a melee attack, 1095// so it tries to close as fast as possible 1096// 1097 1098void A_Chase(mobj_t *actor) 1099{ 1100 if (actor->reactiontime) 1101 actor->reactiontime--; 1102 1103 if (actor->threshold) { /* modify target threshold */ 1104 if (!actor->target || actor->target->health <= 0) 1105 actor->threshold = 0; 1106 else 1107 actor->threshold--; 1108 } 1109 1110 /* turn towards movement direction if not there yet 1111 * killough 9/7/98: keep facing towards target if strafing or backing out 1112 */ 1113 1114 if (actor->strafecount) 1115 A_FaceTarget(actor); 1116 else if (actor->movedir < 8) 1117 { 1118 int delta = (actor->angle &= (7<<29)) - (actor->movedir << 29); 1119 if (delta > 0) 1120 actor->angle -= ANG90/2; 1121 else 1122 if (delta < 0) 1123 actor->angle += ANG90/2; 1124 } 1125 1126 if (!actor->target || !(actor->target->flags&MF_SHOOTABLE)) 1127 { 1128 if (!P_LookForTargets(actor,true)) // look for a new target 1129 P_SetMobjState(actor, actor->info->spawnstate); // no new target 1130 return; 1131 } 1132 1133 // do not attack twice in a row 1134 if (actor->flags & MF_JUSTATTACKED) 1135 { 1136 actor->flags &= ~MF_JUSTATTACKED; 1137 if (gameskill != sk_nightmare && !fastparm) 1138 P_NewChaseDir(actor); 1139 return; 1140 } 1141 1142 // check for melee attack 1143 if (actor->info->meleestate && P_CheckMeleeRange(actor)) 1144 { 1145 if (actor->info->attacksound) 1146 S_StartSound(actor, actor->info->attacksound); 1147 P_SetMobjState(actor, actor->info->meleestate); 1148 /* killough 8/98: remember an attack 1149 * cph - DEMOSYNC? */ 1150 if (!actor->info->missilestate) 1151 actor->flags |= MF_JUSTHIT; 1152 return; 1153 } 1154 1155 // check for missile attack 1156 if (actor->info->missilestate) 1157 if (!(gameskill < sk_nightmare && !fastparm && actor->movecount)) 1158 if (P_CheckMissileRange(actor)) 1159 { 1160 P_SetMobjState(actor, actor->info->missilestate); 1161 actor->flags |= MF_JUSTATTACKED; 1162 return; 1163 } 1164 1165 if (!actor->threshold) { 1166 if (!mbf_features) 1167 { /* killough 9/9/98: for backward demo compatibility */ 1168 if (netgame && !P_CheckSight(actor, actor->target) && 1169 P_LookForPlayers(actor, true)) 1170 return; 1171 } 1172 /* killough 7/18/98, 9/9/98: new monster AI */ 1173 else if (help_friends && P_HelpFriend(actor)) 1174 return; /* killough 9/8/98: Help friends in need */ 1175 /* Look for new targets if current one is bad or is out of view */ 1176 else if (actor->pursuecount) 1177 actor->pursuecount--; 1178 else { 1179 /* Our pursuit time has expired. We're going to think about 1180 * changing targets */ 1181 actor->pursuecount = BASETHRESHOLD; 1182 1183 /* Unless (we have a live target 1184 * and it's not friendly 1185 * and we can see it) 1186 * try to find a new one; return if sucessful */ 1187 1188 if (!(actor->target && actor->target->health > 0 && 1189 ((comp[comp_pursuit] && !netgame) || 1190 (((actor->target->flags ^ actor->flags) & MF_FRIEND || 1191 (!(actor->flags & MF_FRIEND) && monster_infighting)) && 1192 P_CheckSight(actor, actor->target)))) 1193 && P_LookForTargets(actor, true)) 1194 return; 1195 1196 /* (Current target was good, or no new target was found.) 1197 * 1198 * If monster is a missile-less friend, give up pursuit and 1199 * return to player, if no attacks have occurred recently. 1200 */ 1201 1202 if (!actor->info->missilestate && actor->flags & MF_FRIEND) { 1203 if (actor->flags & MF_JUSTHIT) /* if recent action, */ 1204 actor->flags &= ~MF_JUSTHIT; /* keep fighting */ 1205 else if (P_LookForPlayers(actor, true)) /* else return to player */ 1206 return; 1207 } 1208 } 1209 } 1210 1211 if (actor->strafecount) 1212 actor->strafecount--; 1213 1214 // chase towards player 1215 if (--actor->movecount<0 || !P_SmartMove(actor)) 1216 P_NewChaseDir(actor); 1217 1218 // make active sound 1219 if (actor->info->activesound && P_Random(pr_see)<3) 1220 S_StartSound(actor, actor->info->activesound); 1221} 1222 1223// 1224// A_FaceTarget 1225// 1226void A_FaceTarget(mobj_t *actor) 1227{ 1228 if (!actor->target) 1229 return; 1230 actor->flags &= ~MF_AMBUSH; 1231 actor->angle = R_PointToAngle2(actor->x, actor->y, 1232 actor->target->x, actor->target->y); 1233 if (actor->target->flags & MF_SHADOW) 1234 { // killough 5/5/98: remove dependence on order of evaluation: 1235 int t = P_Random(pr_facetarget); 1236 actor->angle += (t-P_Random(pr_facetarget))<<21; 1237 } 1238} 1239 1240// 1241// A_PosAttack 1242// 1243 1244void A_PosAttack(mobj_t *actor) 1245{ 1246 int angle, damage, slope, t; 1247 1248 if (!actor->target) 1249 return; 1250 A_FaceTarget(actor); 1251 angle = actor->angle; 1252 slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */ 1253 S_StartSound(actor, sfx_pistol); 1254 1255 // killough 5/5/98: remove dependence on order of evaluation: 1256 t = P_Random(pr_posattack); 1257 angle += (t - P_Random(pr_posattack))<<20; 1258 damage = (P_Random(pr_posattack)%5 + 1)*3; 1259 P_LineAttack(actor, angle, MISSILERANGE, slope, damage); 1260} 1261 1262void A_SPosAttack(mobj_t* actor) 1263{ 1264 int i, bangle, slope; 1265 1266 if (!actor->target) 1267 return; 1268 S_StartSound(actor, sfx_shotgn); 1269 A_FaceTarget(actor); 1270 bangle = actor->angle; 1271 slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ 1272 for (i=0; i<3; i++) 1273 { // killough 5/5/98: remove dependence on order of evaluation: 1274 int t = P_Random(pr_sposattack); 1275 int angle = bangle + ((t - P_Random(pr_sposattack))<<20); 1276 int damage = ((P_Random(pr_sposattack)%5)+1)*3; 1277 P_LineAttack(actor, angle, MISSILERANGE, slope, damage); 1278 } 1279} 1280 1281void A_CPosAttack(mobj_t *actor) 1282{ 1283 int angle, bangle, damage, slope, t; 1284 1285 if (!actor->target) 1286 return; 1287 S_StartSound(actor, sfx_shotgn); 1288 A_FaceTarget(actor); 1289 bangle = actor->angle; 1290 slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ 1291 1292 // killough 5/5/98: remove dependence on order of evaluation: 1293 t = P_Random(pr_cposattack); 1294 angle = bangle + ((t - P_Random(pr_cposattack))<<20); 1295 damage = ((P_Random(pr_cposattack)%5)+1)*3; 1296 P_LineAttack(actor, angle, MISSILERANGE, slope, damage); 1297} 1298 1299void A_CPosRefire(mobj_t *actor) 1300{ 1301 // keep firing unless target got out of sight 1302 A_FaceTarget(actor); 1303 1304 /* killough 12/98: Stop firing if a friend has gotten in the way */ 1305 if (P_HitFriend(actor)) 1306 goto stop; 1307 1308 /* killough 11/98: prevent refiring on friends continuously */ 1309 if (P_Random(pr_cposrefire) < 40) { 1310 if (actor->target && actor->flags & actor->target->flags & MF_FRIEND) 1311 goto stop; 1312 else 1313 return; 1314 } 1315 1316 if (!actor->target || actor->target->health <= 0 1317 || !P_CheckSight(actor, actor->target)) 1318stop: P_SetMobjState(actor, actor->info->seestate); 1319} 1320 1321void A_SpidRefire(mobj_t* actor) 1322{ 1323 // keep firing unless target got out of sight 1324 A_FaceTarget(actor); 1325 1326 /* killough 12/98: Stop firing if a friend has gotten in the way */ 1327 if (P_HitFriend(actor)) 1328 goto stop; 1329 1330 if (P_Random(pr_spidrefire) < 10) 1331 return; 1332 1333 // killough 11/98: prevent refiring on friends continuously 1334 if (!actor->target || actor->target->health <= 0 1335 || actor->flags & actor->target->flags & MF_FRIEND 1336 || !P_CheckSight(actor, actor->target)) 1337stop: P_SetMobjState(actor, actor->info->seestate); 1338} 1339 1340void A_BspiAttack(mobj_t *actor) 1341{ 1342 if (!actor->target) 1343 return; 1344 A_FaceTarget(actor); 1345 P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile 1346} 1347 1348// 1349// A_TroopAttack 1350// 1351 1352void A_TroopAttack(mobj_t *actor) 1353{ 1354 if (!actor->target) 1355 return; 1356 A_FaceTarget(actor); 1357 if (P_CheckMeleeRange(actor)) 1358 { 1359 int damage; 1360 S_StartSound(actor, sfx_claw); 1361 damage = (P_Random(pr_troopattack)%8+1)*3; 1362 P_DamageMobj(actor->target, actor, actor, damage); 1363 return; 1364 } 1365 P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile 1366} 1367 1368void A_SargAttack(mobj_t *actor) 1369{ 1370 if (!actor->target) 1371 return; 1372 A_FaceTarget(actor); 1373 if (P_CheckMeleeRange(actor)) 1374 { 1375 int damage = ((P_Random(pr_sargattack)%10)+1)*4; 1376 P_DamageMobj(actor->target, actor, actor, damage); 1377 } 1378} 1379 1380void A_HeadAttack(mobj_t *actor) 1381{ 1382 if (!actor->target) 1383 return; 1384 A_FaceTarget (actor); 1385 if (P_CheckMeleeRange(actor)) 1386 { 1387 int damage = (P_Random(pr_headattack)%6+1)*10; 1388 P_DamageMobj(actor->target, actor, actor, damage); 1389 return; 1390 } 1391 P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile 1392} 1393 1394void A_CyberAttack(mobj_t *actor) 1395{ 1396 if (!actor->target) 1397 return; 1398 A_FaceTarget(actor); 1399 P_SpawnMissile(actor, actor->target, MT_ROCKET); 1400} 1401 1402void A_BruisAttack(mobj_t *actor) 1403{ 1404 if (!actor->target) 1405 return; 1406 if (P_CheckMeleeRange(actor)) 1407 { 1408 int damage; 1409 S_StartSound(actor, sfx_claw); 1410 damage = (P_Random(pr_bruisattack)%8+1)*10; 1411 P_DamageMobj(actor->target, actor, actor, damage); 1412 return; 1413 } 1414 P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile 1415} 1416 1417// 1418// A_SkelMissile 1419// 1420 1421void A_SkelMissile(mobj_t *actor) 1422{ 1423 mobj_t *mo; 1424 1425 if (!actor->target) 1426 return; 1427 1428 A_FaceTarget (actor); 1429 actor->z += 16*FRACUNIT; // so missile spawns higher 1430 mo = P_SpawnMissile (actor, actor->target, MT_TRACER); 1431 actor->z -= 16*FRACUNIT; // back to normal 1432 1433 mo->x += mo->momx; 1434 mo->y += mo->momy; 1435 P_SetTarget(&mo->tracer, actor->target); 1436} 1437 1438int TRACEANGLE = 0xc000000; 1439 1440void A_Tracer(mobj_t *actor) 1441{ 1442 angle_t exact; 1443 fixed_t dist; 1444 fixed_t slope; 1445 mobj_t *dest; 1446 mobj_t *th; 1447 1448 /* killough 1/18/98: this is why some missiles do not have smoke 1449 * and some do. Also, internal demos start at random gametics, thus 1450 * the bug in which revenants cause internal demos to go out of sync. 1451 * 1452 * killough 3/6/98: fix revenant internal demo bug by subtracting 1453 * levelstarttic from gametic. 1454 * 1455 * killough 9/29/98: use new "basetic" so that demos stay in sync 1456 * during pauses and menu activations, while retaining old demo sync. 1457 * 1458 * leveltime would have been better to use to start with in Doom, but 1459 * since old demos were recorded using gametic, we must stick with it, 1460 * and improvise around it (using leveltime causes desync across levels). 1461 */ 1462 1463 if ((gametic-basetic) & 3) 1464 return; 1465 1466 // spawn a puff of smoke behind the rocket 1467 P_SpawnPuff(actor->x, actor->y, actor->z); 1468 1469 th = P_SpawnMobj (actor->x-actor->momx, 1470 actor->y-actor->momy, 1471 actor->z, MT_SMOKE); 1472 1473 th->momz = FRACUNIT; 1474 th->tics -= P_Random(pr_tracer) & 3; 1475 if (th->tics < 1) 1476 th->tics = 1; 1477 1478 // adjust direction 1479 dest = actor->tracer; 1480 1481 if (!dest || dest->health <= 0) 1482 return; 1483 1484 // change angle 1485 exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); 1486 1487 if (exact != actor->angle) { 1488 if (exact - actor->angle > 0x80000000) 1489 { 1490 actor->angle -= TRACEANGLE; 1491 if (exact - actor->angle < 0x80000000) 1492 actor->angle = exact; 1493 } 1494 else 1495 { 1496 actor->angle += TRACEANGLE; 1497 if (exact - actor->angle > 0x80000000) 1498 actor->angle = exact; 1499 } 1500 } 1501 1502 exact = actor->angle>>ANGLETOFINESHIFT; 1503 actor->momx = FixedMul(actor->info->speed, finecosine[exact]); 1504 actor->momy = FixedMul(actor->info->speed, finesine[exact]); 1505 1506 // change slope 1507 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); 1508 1509 dist = dist / actor->info->speed; 1510 1511 if (dist < 1) 1512 dist = 1; 1513 1514 slope = (dest->z+40*FRACUNIT - actor->z) / dist; 1515 1516 if (slope < actor->momz) 1517 actor->momz -= FRACUNIT/8; 1518 else 1519 actor->momz += FRACUNIT/8; 1520} 1521 1522void A_SkelWhoosh(mobj_t *actor) 1523{ 1524 if (!actor->target) 1525 return; 1526 A_FaceTarget(actor); 1527 S_StartSound(actor,sfx_skeswg); 1528} 1529 1530void A_SkelFist(mobj_t *actor) 1531{ 1532 if (!actor->target) 1533 return; 1534 A_FaceTarget(actor); 1535 if (P_CheckMeleeRange(actor)) 1536 { 1537 int damage = ((P_Random(pr_skelfist)%10)+1)*6; 1538 S_StartSound(actor, sfx_skepch); 1539 P_DamageMobj(actor->target, actor, actor, damage); 1540 } 1541} 1542 1543// 1544// PIT_VileCheck 1545// Detect a corpse that could be raised. 1546// 1547 1548mobj_t* corpsehit; 1549mobj_t* vileobj; 1550fixed_t viletryx; 1551fixed_t viletryy; 1552 1553boolean PIT_VileCheck(mobj_t *thing) 1554{ 1555 int maxdist; 1556 boolean check; 1557 1558 if (!(thing->flags & MF_CORPSE) ) 1559 return true; // not a monster 1560 1561 if (thing->tics != -1) 1562 return true; // not lying still yet 1563 1564 if (thing->info->raisestate == S_NULL) 1565 return true; // monster doesn't have a raise state 1566 1567 maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; 1568 1569 if (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist) 1570 return true; // not actually touching 1571 1572 // Check to see if the radius and height are zero. If they are // phares 1573 // then this is a crushed monster that has been turned into a // | 1574 // gib. One of the options may be to ignore this guy. // V 1575 1576 // Option 1: the original, buggy method, -> ghost (compatibility) 1577 // Option 2: ressurect the monster, but not as a ghost 1578 // Option 3: ignore the gib 1579 1580 // if (Option3) // ^ 1581 // if ((thing->height == 0) && (thing->radius == 0)) // | 1582 // return true; // phares 1583 1584 corpsehit = thing; 1585 corpsehit->momx = corpsehit->momy = 0; 1586 if (comp[comp_vile]) // phares 1587 { // | 1588 corpsehit->height <<= 2; // V 1589 check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); 1590 corpsehit->height >>= 2; 1591 } 1592 else 1593 { 1594 int height,radius; 1595 1596 height = corpsehit->height; // save temporarily 1597 radius = corpsehit->radius; // save temporarily 1598 corpsehit->height = corpsehit->info->height; 1599 corpsehit->radius = corpsehit->info->radius; 1600 corpsehit->flags |= MF_SOLID; 1601 check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); 1602 corpsehit->height = height; // restore 1603 corpsehit->radius = radius; // restore // ^ 1604 corpsehit->flags &= ~MF_SOLID; 1605 } // | 1606 // phares 1607 if (!check) 1608 return true; // doesn't fit here 1609 return false; // got one, so stop checking 1610} 1611 1612// 1613// A_VileChase 1614// Check for ressurecting a body 1615// 1616 1617void A_VileChase(mobj_t* actor) 1618{ 1619 int xl, xh; 1620 int yl, yh; 1621 int bx, by; 1622 1623 if (actor->movedir != DI_NODIR) 1624 { 1625 // check for corpses to raise 1626 viletryx = 1627 actor->x + actor->info->speed*xspeed[actor->movedir]; 1628 viletryy = 1629 actor->y + actor->info->speed*yspeed[actor->movedir]; 1630 1631 xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT; 1632 xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT; 1633 yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT; 1634 yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT; 1635 1636 vileobj = actor; 1637 for (bx=xl ; bx<=xh ; bx++) 1638 { 1639 for (by=yl ; by<=yh ; by++) 1640 { 1641 // Call PIT_VileCheck to check 1642 // whether object is a corpse 1643 // that canbe raised. 1644 if (!P_BlockThingsIterator(bx,by,PIT_VileCheck)) 1645 { 1646 mobjinfo_t *info; 1647 1648 // got one! 1649 mobj_t* temp = actor->target; 1650 actor->target = corpsehit; 1651 A_FaceTarget(actor); 1652 actor->target = temp; 1653 1654 P_SetMobjState(actor, S_VILE_HEAL1); 1655 S_StartSound(corpsehit, sfx_slop); 1656 info = corpsehit->info; 1657 1658 P_SetMobjState(corpsehit,info->raisestate); 1659 1660 if (comp[comp_vile]) // phares 1661 corpsehit->height <<= 2; // | 1662 else // V 1663 { 1664 corpsehit->height = info->height; // fix Ghost bug 1665 corpsehit->radius = info->radius; // fix Ghost bug 1666 } // phares 1667 1668 /* killough 7/18/98: 1669 * friendliness is transferred from AV to raised corpse 1670 */ 1671 corpsehit->flags = 1672 (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); 1673 1674 if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) 1675 totallive++; 1676 1677 corpsehit->health = info->spawnhealth; 1678 P_SetTarget(&corpsehit->target, NULL); // killough 11/98 1679 1680 if (mbf_features) 1681 { /* kilough 9/9/98 */ 1682 P_SetTarget(&corpsehit->lastenemy, NULL); 1683 corpsehit->flags &= ~MF_JUSTHIT; 1684 } 1685 1686 /* killough 8/29/98: add to appropriate thread */ 1687 P_UpdateThinker(&corpsehit->thinker); 1688 1689 return; 1690 } 1691 } 1692 } 1693 } 1694 A_Chase(actor); // Return to normal attack. 1695} 1696 1697// 1698// A_VileStart 1699// 1700 1701void A_VileStart(mobj_t *actor) 1702{ 1703 S_StartSound(actor, sfx_vilatk); 1704} 1705 1706// 1707// A_Fire 1708// Keep fire in front of player unless out of sight 1709// 1710 1711void A_Fire(mobj_t *actor); 1712 1713void A_StartFire(mobj_t *actor) 1714{ 1715 S_StartSound(actor,sfx_flamst); 1716 A_Fire(actor); 1717} 1718 1719void A_FireCrackle(mobj_t* actor) 1720{ 1721 S_StartSound(actor,sfx_flame); 1722 A_Fire(actor); 1723} 1724 1725void A_Fire(mobj_t *actor) 1726{ 1727 unsigned an; 1728 mobj_t *dest = actor->tracer; 1729 1730 if (!dest) 1731 return; 1732 1733 // don't move it if the vile lost sight 1734 if (!P_CheckSight(actor->target, dest) ) 1735 return; 1736 1737 an = dest->angle >> ANGLETOFINESHIFT; 1738 1739 P_UnsetThingPosition(actor); 1740 actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]); 1741 actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]); 1742 actor->z = dest->z; 1743 P_SetThingPosition(actor); 1744} 1745 1746// 1747// A_VileTarget 1748// Spawn the hellfire 1749// 1750 1751void A_VileTarget(mobj_t *actor) 1752{ 1753 mobj_t *fog; 1754 1755 if (!actor->target) 1756 return; 1757 1758 A_FaceTarget(actor); 1759 1760 // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned 1761 fog = P_SpawnMobj(actor->target->x, 1762 (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y, 1763 actor->target->z,MT_FIRE); 1764 1765 P_SetTarget(&actor->tracer, fog); 1766 P_SetTarget(&fog->target, actor); 1767 P_SetTarget(&fog->tracer, actor->target); 1768 A_Fire(fog); 1769} 1770 1771// 1772// A_VileAttack 1773// 1774 1775void A_VileAttack(mobj_t *actor) 1776{ 1777 mobj_t *fire; 1778 int an; 1779 1780 if (!actor->target) 1781 return; 1782 1783 A_FaceTarget(actor); 1784 1785 if (!P_CheckSight(actor, actor->target)) 1786 return; 1787 1788 S_StartSound(actor, sfx_barexp); 1789 P_DamageMobj(actor->target, actor, actor, 20); 1790 actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; 1791 1792 an = actor->angle >> ANGLETOFINESHIFT; 1793 1794 fire = actor->tracer; 1795 1796 if (!fire) 1797 return; 1798 1799 // move the fire between the vile and the player 1800 fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); 1801 fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); 1802 P_RadiusAttack(fire, actor, 70); 1803} 1804 1805// 1806// Mancubus attack, 1807// firing three missiles (bruisers) 1808// in three different directions? 1809// Doesn't look like it. 1810// 1811 1812#define FATSPREAD (ANG90/8) 1813 1814void A_FatRaise(mobj_t *actor) 1815{ 1816 A_FaceTarget(actor); 1817 S_StartSound(actor, sfx_manatk); 1818} 1819 1820void A_FatAttack1(mobj_t *actor) 1821{ 1822 mobj_t *mo; 1823 int an; 1824 1825 A_FaceTarget(actor); 1826 1827 // Change direction to ... 1828 actor->angle += FATSPREAD; 1829 1830 P_SpawnMissile(actor, actor->target, MT_FATSHOT); 1831 1832 mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT); 1833 mo->angle += FATSPREAD; 1834 an = mo->angle >> ANGLETOFINESHIFT; 1835 mo->momx = FixedMul(mo->info->speed, finecosine[an]); 1836 mo->momy = FixedMul(mo->info->speed, finesine[an]); 1837} 1838 1839void A_FatAttack2(mobj_t *actor) 1840{ 1841 mobj_t *mo; 1842 int an; 1843 1844 A_FaceTarget(actor); 1845 // Now here choose opposite deviation. 1846 actor->angle -= FATSPREAD; 1847 P_SpawnMissile(actor, actor->target, MT_FATSHOT); 1848 1849 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); 1850 mo->angle -= FATSPREAD*2; 1851 an = mo->angle >> ANGLETOFINESHIFT; 1852 mo->momx = FixedMul(mo->info->speed, finecosine[an]); 1853 mo->momy = FixedMul(mo->info->speed, finesine[an]); 1854} 1855 1856void A_FatAttack3(mobj_t *actor) 1857{ 1858 mobj_t *mo; 1859 int an; 1860 1861 A_FaceTarget(actor); 1862 1863 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); 1864 mo->angle -= FATSPREAD/2; 1865 an = mo->angle >> ANGLETOFINESHIFT; 1866 mo->momx = FixedMul(mo->info->speed, finecosine[an]); 1867 mo->momy = FixedMul(mo->info->speed, finesine[an]); 1868 1869 mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); 1870 mo->angle += FATSPREAD/2; 1871 an = mo->angle >> ANGLETOFINESHIFT; 1872 mo->momx = FixedMul(mo->info->speed, finecosine[an]); 1873 mo->momy = FixedMul(mo->info->speed, finesine[an]); 1874} 1875 1876 1877// 1878// SkullAttack 1879// Fly at the player like a missile. 1880// 1881#define SKULLSPEED (20*FRACUNIT) 1882 1883void A_SkullAttack(mobj_t *actor) 1884{ 1885 mobj_t *dest; 1886 angle_t an; 1887 int dist; 1888 1889 if (!actor->target) 1890 return; 1891 1892 dest = actor->target; 1893 actor->flags |= MF_SKULLFLY; 1894 1895 S_StartSound(actor, actor->info->attacksound); 1896 A_FaceTarget(actor); 1897 an = actor->angle >> ANGLETOFINESHIFT; 1898 actor->momx = FixedMul(SKULLSPEED, finecosine[an]); 1899 actor->momy = FixedMul(SKULLSPEED, finesine[an]); 1900 dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); 1901 dist = dist / SKULLSPEED; 1902 1903 if (dist < 1) 1904 dist = 1; 1905 actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist; 1906} 1907 1908// 1909// A_PainShootSkull 1910// Spawn a lost soul and launch it at the target 1911// 1912 1913void A_PainShootSkull(mobj_t *actor, angle_t angle) 1914{ 1915 fixed_t x,y,z; 1916 mobj_t *newmobj; 1917 angle_t an; 1918 int prestep; 1919 1920 // The original code checked for 20 skulls on the level, // phares 1921 // and wouldn't spit another one if there were. If not in // phares 1922 // compatibility mode, we remove the limit. // phares 1923 // phares 1924 if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */ 1925 { 1926 // count total number of skulls currently on the level 1927 int count = 0; 1928 thinker_t *currentthinker; 1929 for (currentthinker = thinkercap.next; 1930 currentthinker != &thinkercap; 1931 currentthinker = currentthinker->next) 1932 if ((currentthinker->function == P_MobjThinker) 1933 && ((mobj_t *)currentthinker)->type == MT_SKULL) 1934 count++; 1935 if (count > 20) // phares 1936 return; // phares 1937 } 1938 1939 // okay, there's room for another one 1940 1941 an = angle >> ANGLETOFINESHIFT; 1942 1943 prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2; 1944 1945 x = actor->x + FixedMul(prestep, finecosine[an]); 1946 y = actor->y + FixedMul(prestep, finesine[an]); 1947 z = actor->z + 8*FRACUNIT; 1948 1949 if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */ 1950 newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares 1951 else // V 1952 { 1953 // Check whether the Lost Soul is being fired through a 1-sided 1954 // wall or an impassible line, or a "monsters can't cross" line. 1955 // If it is, then we don't allow the spawn. This is a bug fix, but 1956 // it should be considered an enhancement, since it may disturb 1957 // existing demos, so don't do it in compatibility mode. 1958 1959 if (Check_Sides(actor,x,y)) 1960 return; 1961 1962 newmobj = P_SpawnMobj(x, y, z, MT_SKULL); 1963 1964 // Check to see if the new Lost Soul's z value is above the 1965 // ceiling of its new sector, or below the floor. If so, kill it. 1966 1967 if ((newmobj->z > 1968 (newmobj->subsector->sector->ceilingheight - newmobj->height)) || 1969 (newmobj->z < newmobj->subsector->sector->floorheight)) 1970 { 1971 // kill it immediately 1972 P_DamageMobj(newmobj,actor,actor,10000); 1973 return; // ^ 1974 } // | 1975 } // phares 1976 1977 /* killough 7/20/98: PEs shoot lost souls with the same friendliness */ 1978 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); 1979 1980 /* killough 8/29/98: add to appropriate thread */ 1981 P_UpdateThinker(&newmobj->thinker); 1982 1983 // Check for movements. 1984 // killough 3/15/98: don't jump over dropoffs: 1985 1986 if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false)) 1987 { 1988 // kill it immediately 1989 P_DamageMobj(newmobj, actor, actor, 10000); 1990 return; 1991 } 1992 1993 P_SetTarget(&newmobj->target, actor->target); 1994 A_SkullAttack(newmobj); 1995} 1996 1997// 1998// A_PainAttack 1999// Spawn a lost soul and launch it at the target 2000// 2001 2002void A_PainAttack(mobj_t *actor) 2003{ 2004 if (!actor->target) 2005 return; 2006 A_FaceTarget(actor); 2007 A_PainShootSkull(actor, actor->angle); 2008} 2009 2010void A_PainDie(mobj_t *actor) 2011{ 2012 A_Fall(actor); 2013 A_PainShootSkull(actor, actor->angle+ANG90); 2014 A_PainShootSkull(actor, actor->angle+ANG180); 2015 A_PainShootSkull(actor, actor->angle+ANG270); 2016} 2017 2018void A_Scream(mobj_t *actor) 2019{ 2020 int sound; 2021 2022 switch (actor->info->deathsound) 2023 { 2024 case 0: 2025 return; 2026 2027 case sfx_podth1: 2028 case sfx_podth2: 2029 case sfx_podth3: 2030 sound = sfx_podth1 + P_Random(pr_scream)%3; 2031 break; 2032 2033 case sfx_bgdth1: 2034 case sfx_bgdth2: 2035 sound = sfx_bgdth1 + P_Random(pr_scream)%2; 2036 break; 2037 2038 default: 2039 sound = actor->info->deathsound; 2040 break; 2041 } 2042 2043 // Check for bosses. 2044 if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) 2045 S_StartSound(NULL, sound); // full volume 2046 else 2047 S_StartSound(actor, sound); 2048} 2049 2050void A_XScream(mobj_t *actor) 2051{ 2052 S_StartSound(actor, sfx_slop); 2053} 2054 2055void A_Pain(mobj_t *actor) 2056{ 2057 if (actor->info->painsound) 2058 S_StartSound(actor, actor->info->painsound); 2059} 2060 2061void A_Fall(mobj_t *actor) 2062{ 2063 // actor is on ground, it can be walked over 2064 actor->flags &= ~MF_SOLID; 2065} 2066 2067// 2068// A_Explode 2069// 2070void A_Explode(mobj_t *thingy) 2071{ 2072 P_RadiusAttack( thingy, thingy->target, 128 ); 2073} 2074 2075// 2076// A_BossDeath 2077// Possibly trigger special effects 2078// if on first boss level 2079// 2080 2081void A_BossDeath(mobj_t *mo) 2082{ 2083 thinker_t *th; 2084 line_t junk; 2085 int i; 2086 2087 if (gamemode == commercial) 2088 { 2089 if (gamemap != 7) 2090 return; 2091 2092 if ((mo->type != MT_FATSO) 2093 && (mo->type != MT_BABY)) 2094 return; 2095 } 2096 else 2097 { 2098 switch(gameepisode) 2099 { 2100 case 1: 2101 if (gamemap != 8) 2102 return; 2103 2104 if (mo->type != MT_BRUISER) 2105 return; 2106 break; 2107 2108 case 2: 2109 if (gamemap != 8) 2110 return; 2111 2112 if (mo->type != MT_CYBORG) 2113 return; 2114 break; 2115 2116 case 3: 2117 if (gamemap != 8) 2118 return; 2119 2120 if (mo->type != MT_SPIDER) 2121 return; 2122 2123 break; 2124 2125 case 4: 2126 switch(gamemap) 2127 { 2128 case 6: 2129 if (mo->type != MT_CYBORG) 2130 return; 2131 break; 2132 2133 case 8: 2134 if (mo->type != MT_SPIDER) 2135 return; 2136 break; 2137 2138 default: 2139 return; 2140 break; 2141 } 2142 break; 2143 2144 default: 2145 if (gamemap != 8) 2146 return; 2147 break; 2148 } 2149 2150 } 2151 2152 // make sure there is a player alive for victory 2153 for (i=0; i<MAXPLAYERS; i++) 2154 if (playeringame[i] && players[i].health > 0) 2155 break; 2156 2157 if (i==MAXPLAYERS) 2158 return; // no one left alive, so do not end game 2159 2160 // scan the remaining thinkers to see 2161 // if all bosses are dead 2162 for (th = thinkercap.next ; th != &thinkercap ; th=th->next) 2163 if (th->function == P_MobjThinker) 2164 { 2165 mobj_t *mo2 = (mobj_t *) th; 2166 if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) 2167 return; // other boss not dead 2168 } 2169 2170 // victory! 2171 if ( gamemode == commercial) 2172 { 2173 if (gamemap == 7) 2174 { 2175 if (mo->type == MT_FATSO) 2176 { 2177 junk.tag = 666; 2178 EV_DoFloor(&junk,lowerFloorToLowest); 2179 return; 2180 } 2181 2182 if (mo->type == MT_BABY) 2183 { 2184 junk.tag = 667; 2185 EV_DoFloor(&junk,raiseToTexture); 2186 return; 2187 } 2188 } 2189 } 2190 else 2191 { 2192 switch(gameepisode) 2193 { 2194 case 1: 2195 junk.tag = 666; 2196 EV_DoFloor(&junk, lowerFloorToLowest); 2197 return; 2198 break; 2199 2200 case 4: 2201 switch(gamemap) 2202 { 2203 case 6: 2204 junk.tag = 666; 2205 EV_DoDoor(&junk, blazeOpen); 2206 return; 2207 break; 2208 2209 case 8: 2210 junk.tag = 666; 2211 EV_DoFloor(&junk, lowerFloorToLowest); 2212 return; 2213 break; 2214 } 2215 } 2216 } 2217 G_ExitLevel(); 2218} 2219 2220 2221void A_Hoof (mobj_t* mo) 2222{ 2223 S_StartSound(mo, sfx_hoof); 2224 A_Chase(mo); 2225} 2226 2227void A_Metal(mobj_t *mo) 2228{ 2229 S_StartSound(mo, sfx_metal); 2230 A_Chase(mo); 2231} 2232 2233void A_BabyMetal(mobj_t *mo) 2234{ 2235 S_StartSound(mo, sfx_bspwlk); 2236 A_Chase(mo); 2237} 2238 2239void A_OpenShotgun2(player_t *player, pspdef_t *psp) 2240{ 2241 (void)psp; 2242 S_StartSound(player->mo, sfx_dbopn); 2243} 2244 2245void A_LoadShotgun2(player_t *player, pspdef_t *psp) 2246{ 2247 (void)psp; 2248 S_StartSound(player->mo, sfx_dbload); 2249} 2250 2251void A_ReFire(player_t *player, pspdef_t *psp); 2252 2253void A_CloseShotgun2(player_t *player, pspdef_t *psp) 2254{ 2255 S_StartSound(player->mo, sfx_dbcls); 2256 A_ReFire(player,psp); 2257} 2258 2259// killough 2/7/98: Remove limit on icon landings: 2260mobj_t **braintargets; 2261int numbraintargets_alloc; 2262int numbraintargets; 2263 2264struct brain_s brain; // killough 3/26/98: global state of boss brain 2265 2266// killough 3/26/98: initialize icon landings at level startup, 2267// rather than at boss wakeup, to prevent savegame-related crashes 2268 2269void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function 2270{ 2271 thinker_t *thinker; 2272 2273 // find all the target spots 2274 numbraintargets = 0; 2275 brain.targeton = 0; 2276 brain.easy = 0; // killough 3/26/98: always init easy to 0 2277 2278 for (thinker = thinkercap.next ; 2279 thinker != &thinkercap ; 2280 thinker = thinker->next) 2281 if (thinker->function == P_MobjThinker) 2282 { 2283 mobj_t *m = (mobj_t *) thinker; 2284 2285 if (m->type == MT_BOSSTARGET ) 2286 { // killough 2/7/98: remove limit on icon landings: 2287 if (numbraintargets >= numbraintargets_alloc) 2288 braintargets = realloc(braintargets, 2289 (numbraintargets_alloc = numbraintargets_alloc ? 2290 numbraintargets_alloc*2 : 32) *sizeof *braintargets); 2291 braintargets[numbraintargets++] = m; 2292 } 2293 } 2294} 2295 2296void A_BrainAwake(mobj_t *mo) 2297{ 2298 (void)mo; 2299 S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now 2300} 2301 2302void A_BrainPain(mobj_t *mo) 2303{ 2304 (void)mo; 2305 S_StartSound(NULL,sfx_bospn); 2306} 2307 2308void A_BrainScream(mobj_t *mo) 2309{ 2310 int x; 2311 for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) 2312 { 2313 int y = mo->y - 320*FRACUNIT; 2314 int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT; 2315 mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET); 2316 th->momz = P_Random(pr_brainscream)*512; 2317 P_SetMobjState(th, S_BRAINEXPLODE1); 2318 th->tics -= P_Random(pr_brainscream)&7; 2319 if (th->tics < 1) 2320 th->tics = 1; 2321 } 2322 S_StartSound(NULL,sfx_bosdth); 2323} 2324 2325void A_BrainExplode(mobj_t *mo) 2326{ // killough 5/5/98: remove dependence on order of evaluation: 2327 (void)mo; 2328 int t = P_Random(pr_brainexp); 2329 int x = mo->x + (t - P_Random(pr_brainexp))*2048; 2330 int y = mo->y; 2331 int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT; 2332 mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET); 2333 th->momz = P_Random(pr_brainexp)*512; 2334 P_SetMobjState(th, S_BRAINEXPLODE1); 2335 th->tics -= P_Random(pr_brainexp)&7; 2336 if (th->tics < 1) 2337 th->tics = 1; 2338} 2339 2340void A_BrainDie(mobj_t *mo) 2341{ 2342 (void)mo; 2343 G_ExitLevel(); 2344} 2345 2346void A_BrainSpit(mobj_t *mo) 2347{ 2348 mobj_t *targ, *newmobj; 2349 2350 if (!numbraintargets) // killough 4/1/98: ignore if no targets 2351 return; 2352 2353 brain.easy ^= 1; // killough 3/26/98: use brain struct 2354 if (gameskill <= sk_easy && !brain.easy) 2355 return; 2356 2357 // shoot a cube at current target 2358 targ = braintargets[brain.targeton++]; // killough 3/26/98: 2359 brain.targeton %= numbraintargets; // Use brain struct for targets 2360 2361 // spawn brain missile 2362 newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); 2363 P_SetTarget(&newmobj->target, targ); 2364 newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics); 2365 2366 // killough 7/18/98: brain friendliness is transferred 2367 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); 2368 2369 // killough 8/29/98: add to appropriate thread 2370 P_UpdateThinker(&newmobj->thinker); 2371 2372 S_StartSound(NULL, sfx_bospit); 2373} 2374 2375void A_SpawnFly(mobj_t *mo); 2376 2377// travelling cube sound 2378void A_SpawnSound(mobj_t *mo) 2379{ 2380 S_StartSound(mo,sfx_boscub); 2381 A_SpawnFly(mo); 2382} 2383 2384void A_SpawnFly(mobj_t *mo) 2385{ 2386 mobj_t *newmobj; 2387 mobj_t *fog; 2388 mobj_t *targ; 2389 int r; 2390 mobjtype_t type; 2391 2392 if (--mo->reactiontime) 2393 return; // still flying 2394 2395 targ = mo->target; 2396 2397 // First spawn teleport fog. 2398 fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); 2399 S_StartSound(fog, sfx_telept); 2400 2401 // Randomly select monster to spawn. 2402 r = P_Random(pr_spawnfly); 2403 2404 // Probability distribution (kind of :), decreasing likelihood. 2405 if ( r<50 ) 2406 type = MT_TROOP; 2407 else if (r<90) 2408 type = MT_SERGEANT; 2409 else if (r<120) 2410 type = MT_SHADOWS; 2411 else if (r<130) 2412 type = MT_PAIN; 2413 else if (r<160) 2414 type = MT_HEAD; 2415 else if (r<162) 2416 type = MT_VILE; 2417 else if (r<172) 2418 type = MT_UNDEAD; 2419 else if (r<192) 2420 type = MT_BABY; 2421 else if (r<222) 2422 type = MT_FATSO; 2423 else if (r<246) 2424 type = MT_KNIGHT; 2425 else 2426 type = MT_BRUISER; 2427 2428 newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); 2429 2430 /* killough 7/18/98: brain friendliness is transferred */ 2431 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); 2432 2433 /* killough 8/29/98: add to appropriate thread */ 2434 P_UpdateThinker(&newmobj->thinker); 2435 2436 if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */ 2437 P_SetMobjState(newmobj, newmobj->info->seestate); 2438 2439 // telefrag anything in this spot 2440 P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */ 2441 2442 // remove self (i.e., cube). 2443 P_RemoveMobj(mo); 2444} 2445 2446void A_PlayerScream(mobj_t *mo) 2447{ 2448 int sound = sfx_pldeth; // Default death sound. 2449 if (gamemode != shareware && mo->health < -50) 2450 sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING 2451 S_StartSound(mo, sound); 2452} 2453 2454/* cph - MBF-added codepointer functions */ 2455 2456// killough 11/98: kill an object 2457void A_Die(mobj_t *actor) 2458{ 2459 P_DamageMobj(actor, NULL, NULL, actor->health); 2460} 2461 2462// 2463// A_Detonate 2464// killough 8/9/98: same as A_Explode, except that the damage is variable 2465// 2466 2467void A_Detonate(mobj_t *mo) 2468{ 2469 P_RadiusAttack(mo, mo->target, mo->info->damage); 2470} 2471 2472// 2473// killough 9/98: a mushroom explosion effect, sorta :) 2474// Original idea: Linguica 2475// 2476 2477void A_Mushroom(mobj_t *actor) 2478{ 2479 int i, j, n = actor->info->damage; 2480 2481 A_Explode(actor); // First make normal explosion 2482 2483 // Now launch mushroom cloud 2484 for (i = -n; i <= n; i += 8) 2485 for (j = -n; j <= n; j += 8) 2486 { 2487 mobj_t target = *actor, *mo; 2488 target.x += i << FRACBITS; // Aim in many directions from source 2489 target.y += j << FRACBITS; 2490 target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high 2491 mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball 2492 mo->momx >>= 1; 2493 mo->momy >>= 1; // Slow it down a bit 2494 mo->momz >>= 1; 2495 mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity 2496 } 2497} 2498 2499// 2500// killough 11/98 2501// 2502// The following were inspired by Len Pitre 2503// 2504// A small set of highly-sought-after code pointers 2505// 2506 2507void A_Spawn(mobj_t *mo) 2508{ 2509 if (mo->state->misc1) 2510 { 2511 /* mobj_t *newmobj = */ 2512 P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z, 2513 mo->state->misc1 - 1); 2514 /* CPhipps - no friendlyness (yet) 2515 newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); 2516 */ 2517 } 2518} 2519 2520void A_Turn(mobj_t *mo) 2521{ 2522 mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); 2523} 2524 2525void A_Face(mobj_t *mo) 2526{ 2527 mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); 2528} 2529 2530void A_Scratch(mobj_t *mo) 2531{ 2532 mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ? 2533 mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0, 2534 P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0; 2535} 2536 2537void A_PlaySound(mobj_t *mo) 2538{ 2539 S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1); 2540} 2541 2542void A_RandomJump(mobj_t *mo) 2543{ 2544 if (P_Random(pr_randomjump) < mo->state->misc2) 2545 P_SetMobjState(mo, mo->state->misc1); 2546} 2547 2548// 2549// This allows linedef effects to be activated inside deh frames. 2550// 2551 2552void A_LineEffect(mobj_t *mo) 2553{ 2554 static line_t junk; 2555 player_t player; 2556 player_t *oldplayer; 2557 junk = *lines; 2558 oldplayer = mo->player; 2559 mo->player = &player; 2560 player.health = 100; 2561 junk.special = (short)mo->state->misc1; 2562 if (!junk.special) 2563 return; 2564 junk.tag = (short)mo->state->misc2; 2565 if (!P_UseSpecialLine(mo, &junk, 0)) 2566 P_CrossSpecialLine(&junk, 0, mo); 2567 mo->state->misc1 = junk.special; 2568 mo->player = oldplayer; 2569} 2570 2571/***** Start of new functions for Andy Baker's stealth monsters ******/ 2572 2573void P_BecomeVisible(mobj_t* actor) 2574{ 2575 actor->invisible = false; 2576 actor->flags &= ~MF_TRANSLUCBITS; 2577}; 2578 2579void P_IncreaseVisibility(mobj_t* actor) 2580{ 2581 if (actor->invisible) { 2582 actor->invisible = false; 2583 actor->flags |= MF_TRANSLUC25; 2584 } else switch (actor->flags & MF_TRANSLUCBITS) { 2585 case MF_TRANSLUC25: 2586 actor->flags &= ~MF_TRANSLUCBITS; 2587 actor->flags |= MF_TRANSLUC50; 2588 break; 2589 case MF_TRANSLUC50: 2590 actor->flags &= ~MF_TRANSLUCBITS; 2591 actor->flags |= MF_TRANSLUC25; 2592 actor->flags |= MF_TRANSLUC50; 2593 break; 2594 case MF_TRANSLUC75: 2595 actor->flags &= ~MF_TRANSLUCBITS; 2596 break; 2597 } 2598} 2599 2600void P_DecreaseVisibility(mobj_t* actor) 2601{ 2602 if (actor->invisible) 2603 return; // already invisible 2604 2605 switch (actor->flags & MF_TRANSLUCBITS) { 2606 case 0: 2607 actor->flags &= ~MF_TRANSLUCBITS; 2608 actor->flags |= MF_TRANSLUC75; 2609 break; 2610 case MF_TRANSLUC75: 2611 actor->flags &= ~MF_TRANSLUCBITS; 2612 actor->flags |= MF_TRANSLUC50; 2613 break; 2614 case MF_TRANSLUC50: 2615 actor->flags &= ~MF_TRANSLUCBITS; 2616 actor->flags |= MF_TRANSLUC25; 2617 break; 2618 case MF_TRANSLUC25: 2619 actor->flags &= ~MF_TRANSLUCBITS; 2620 actor->invisible = true; 2621 } 2622} 2623/***** End of new functions for Andy Baker's stealth monsters ******/ 2624