1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602 |
- /* Emacs style mode select -*- C++ -*-
- *-----------------------------------------------------------------------------
- *
- *
- * PrBoom: a Doom port merged with LxDoom and LSDLDoom
- * based on BOOM, a modified and improved DOOM engine
- * Copyright (C) 1999 by
- * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
- * Copyright (C) 1999-2000,2002 by
- * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
- * Copyright 2005, 2006 by
- * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- *
- * DESCRIPTION:
- * Enemy thinking, AI.
- * Action Pointer Functions
- * that are associated with states/frames.
- *
- *-----------------------------------------------------------------------------*/
- #include "doomstat.h"
- #include "m_random.h"
- #include "r_main.h"
- #include "p_maputl.h"
- #include "p_map.h"
- #include "p_setup.h"
- #include "p_spec.h"
- #include "s_sound.h"
- #include "sounds.h"
- #include "p_inter.h"
- #include "g_game.h"
- #include "p_enemy.h"
- #include "p_tick.h"
- #include "m_bbox.h"
- #include "lprintf.h"
- static mobj_t *current_actor;
- typedef enum {
- DI_EAST,
- DI_NORTHEAST,
- DI_NORTH,
- DI_NORTHWEST,
- DI_WEST,
- DI_SOUTHWEST,
- DI_SOUTH,
- DI_SOUTHEAST,
- DI_NODIR,
- NUMDIRS
- } dirtype_t;
- static void P_NewChaseDir(mobj_t *actor);
- void P_ZBumpCheck(mobj_t *); // phares
- //
- // ENEMY THINKING
- // Enemies are allways spawned
- // with targetplayer = -1, threshold = 0
- // Most monsters are spawned unaware of all players,
- // but some can be made preaware
- //
- //
- // Called by P_NoiseAlert.
- // Recursively traverse adjacent sectors,
- // sound blocking lines cut off traversal.
- //
- // killough 5/5/98: reformatted, cleaned up
- static void P_RecursiveSound(sector_t *sec, int soundblocks,
- mobj_t *soundtarget)
- {
- int i;
- // wake up all monsters in this sector
- if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1)
- return; // already flooded
- sec->validcount = validcount;
- sec->soundtraversed = soundblocks+1;
- P_SetTarget(&sec->soundtarget, soundtarget);
- for (i=0; i<sec->linecount; i++)
- {
- sector_t *other;
- line_t *check = sec->lines[i];
- if (!(check->flags & ML_TWOSIDED))
- continue;
- P_LineOpening(check);
- if (openrange <= 0)
- continue; // closed door
- other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector;
- if (!(check->flags & ML_SOUNDBLOCK))
- P_RecursiveSound(other, soundblocks, soundtarget);
- else
- if (!soundblocks)
- P_RecursiveSound(other, 1, soundtarget);
- }
- }
- //
- // P_NoiseAlert
- // If a monster yells at a player,
- // it will alert other monsters to the player.
- //
- void P_NoiseAlert(mobj_t *target, mobj_t *emitter)
- {
- validcount++;
- P_RecursiveSound(emitter->subsector->sector, 0, target);
- }
- //
- // P_CheckMeleeRange
- //
- static boolean P_CheckMeleeRange(mobj_t *actor)
- {
- mobj_t *pl = actor->target;
- return // killough 7/18/98: friendly monsters don't attack other friends
- pl && !(actor->flags & pl->flags & MF_FRIEND) &&
- (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) <
- MELEERANGE - 20*FRACUNIT + pl->info->radius) &&
- P_CheckSight(actor, actor->target);
- }
- //
- // P_HitFriend()
- //
- // killough 12/98
- // This function tries to prevent shooting at friends
- static boolean P_HitFriend(mobj_t *actor)
- {
- return actor->flags & MF_FRIEND && actor->target &&
- (P_AimLineAttack(actor,
- R_PointToAngle2(actor->x, actor->y,
- actor->target->x, actor->target->y),
- P_AproxDistance(actor->x-actor->target->x,
- actor->y-actor->target->y), 0),
- linetarget) && linetarget != actor->target &&
- !((linetarget->flags ^ actor->flags) & MF_FRIEND);
- }
- //
- // P_CheckMissileRange
- //
- static boolean P_CheckMissileRange(mobj_t *actor)
- {
- fixed_t dist;
- if (!P_CheckSight(actor, actor->target))
- return false;
- if (actor->flags & MF_JUSTHIT)
- { // the target just hit the enemy, so fight back!
- actor->flags &= ~MF_JUSTHIT;
- /* killough 7/18/98: no friendly fire at corpses
- * killough 11/98: prevent too much infighting among friends
- * cph - yikes, talk about fitting everything on one line... */
- return
- !(actor->flags & MF_FRIEND) ||
- (actor->target->health > 0 &&
- (!(actor->target->flags & MF_FRIEND) ||
- (actor->target->player ?
- monster_infighting || P_Random(pr_defect) >128 :
- !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128)));
- }
- /* killough 7/18/98: friendly monsters don't attack other friendly
- * monsters or players (except when attacked, and then only once)
- */
- if (actor->flags & actor->target->flags & MF_FRIEND)
- return false;
- if (actor->reactiontime)
- return false; // do not attack yet
- // OPTIMIZE: get this from a global checksight
- dist = P_AproxDistance ( actor->x-actor->target->x,
- actor->y-actor->target->y) - 64*FRACUNIT;
- if (!actor->info->meleestate)
- dist -= 128*FRACUNIT; // no melee attack, so fire more
- dist >>= FRACBITS;
- if (actor->type == MT_VILE)
- if (dist > 14*64)
- return false; // too far away
- if (actor->type == MT_UNDEAD)
- {
- if (dist < 196)
- return false; // close for fist attack
- dist >>= 1;
- }
- if (actor->type == MT_CYBORG ||
- actor->type == MT_SPIDER ||
- actor->type == MT_SKULL)
- dist >>= 1;
- if (dist > 200)
- dist = 200;
- if (actor->type == MT_CYBORG && dist > 160)
- dist = 160;
- if (P_Random(pr_missrange) < dist)
- return false;
- if (P_HitFriend(actor))
- return false;
- return true;
- }
- /*
- * P_IsOnLift
- *
- * killough 9/9/98:
- *
- * Returns true if the object is on a lift. Used for AI,
- * since it may indicate the need for crowded conditions,
- * or that a monster should stay on the lift for a while
- * while it goes up or down.
- */
- static boolean P_IsOnLift(const mobj_t *actor)
- {
- const sector_t *sec = actor->subsector->sector;
- line_t line;
- int l;
- // Short-circuit: it's on a lift which is active.
- if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise)
- return true;
- // Check to see if it's in a sector which can be activated as a lift.
- if ((line.tag = sec->tag))
- for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;)
- switch (lines[l].special)
- {
- case 10: case 14: case 15: case 20: case 21: case 22:
- case 47: case 53: case 62: case 66: case 67: case 68:
- case 87: case 88: case 95: case 120: case 121: case 122:
- case 123: case 143: case 162: case 163: case 181: case 182:
- case 144: case 148: case 149: case 211: case 227: case 228:
- case 231: case 232: case 235: case 236:
- return true;
- }
- return false;
- }
- /*
- * P_IsUnderDamage
- *
- * killough 9/9/98:
- *
- * Returns nonzero if the object is under damage based on
- * their current position. Returns 1 if the damage is moderate,
- * -1 if it is serious. Used for AI.
- */
- static int P_IsUnderDamage(mobj_t *actor)
- {
- const struct msecnode_s *seclist;
- const ceiling_t *cl; // Crushing ceiling
- int dir = 0;
- for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext)
- if ((cl = seclist->m_sector->ceilingdata) &&
- cl->thinker.function == T_MoveCeiling)
- dir |= cl->direction;
- return dir;
- }
- //
- // P_Move
- // Move in the current direction,
- // returns false if the move is blocked.
- //
- static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
- static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
- // 1/11/98 killough: Limit removed on special lines crossed
- extern line_t **spechit; // New code -- killough
- extern int numspechit;
- static boolean P_Move(mobj_t *actor, boolean dropoff) /* killough 9/12/98 */
- {
- fixed_t tryx, tryy, deltax, deltay, origx, origy;
- boolean try_ok;
- int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98
- int friction = ORIG_FRICTION;
- int speed;
- if (actor->movedir == DI_NODIR)
- return false;
- #ifdef RANGECHECK
- if ((unsigned)actor->movedir >= 8)
- I_Error ("P_Move: Weird actor->movedir!");
- #endif
- // killough 10/98: make monsters get affected by ice and sludge too:
- if (monster_friction)
- movefactor = P_GetMoveFactor(actor, &friction);
- speed = actor->info->speed;
- if (friction < ORIG_FRICTION && // sludge
- !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2)
- * speed) / ORIG_FRICTION_FACTOR))
- speed = 1; // always give the monster a little bit of speed
- tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]);
- tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]);
- try_ok = P_TryMove(actor, tryx, tryy, dropoff);
- // killough 10/98:
- // Let normal momentum carry them, instead of steptoeing them across ice.
- if (try_ok && friction > ORIG_FRICTION)
- {
- actor->x = origx;
- actor->y = origy;
- movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4;
- actor->momx += FixedMul(deltax, movefactor);
- actor->momy += FixedMul(deltay, movefactor);
- }
- if (!try_ok)
- { // open any specials
- int good;
- if (actor->flags & MF_FLOAT && floatok)
- {
- if (actor->z < tmfloorz) // must adjust height
- actor->z += FLOATSPEED;
- else
- actor->z -= FLOATSPEED;
- actor->flags |= MF_INFLOAT;
- return true;
- }
- if (!numspechit)
- return false;
- actor->movedir = DI_NODIR;
- /* if the special is not a door that can be opened, return false
- *
- * killough 8/9/98: this is what caused monsters to get stuck in
- * doortracks, because it thought that the monster freed itself
- * by opening a door, even if it was moving towards the doortrack,
- * and not the door itself.
- *
- * killough 9/9/98: If a line blocking the monster is activated,
- * return true 90% of the time. If a line blocking the monster is
- * not activated, but some other line is, return false 90% of the
- * time. A bit of randomness is needed to ensure it's free from
- * lockups, but for most cases, it returns the correct result.
- *
- * Do NOT simply return false 1/4th of the time (causes monsters to
- * back out when they shouldn't, and creates secondary stickiness).
- */
- for (good = false; numspechit--; )
- if (P_UseSpecialLine(actor, spechit[numspechit], 0))
- good |= spechit[numspechit] == blockline ? 1 : 2;
- /* cph - compatibility maze here
- * Boom v2.01 and orig. Doom return "good"
- * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3)
- * MBF plays even more games
- */
- if (!good || comp[comp_doorstuck]) return good;
- if (!mbf_features)
- return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */
- else /* finally, MBF code */
- return ((P_Random(pr_opendoor) >= 230) ^ (good & 1));
- }
- else
- actor->flags &= ~MF_INFLOAT;
- /* killough 11/98: fall more slowly, under gravity, if felldown==true */
- if (!(actor->flags & MF_FLOAT) &&
- (!felldown || !mbf_features))
- actor->z = actor->floorz;
- return true;
- }
- /*
- * P_SmartMove
- *
- * killough 9/12/98: Same as P_Move, except smarter
- */
- static boolean P_SmartMove(mobj_t *actor)
- {
- mobj_t *target = actor->target;
- int on_lift, dropoff = false, under_damage;
- /* killough 9/12/98: Stay on a lift if target is on one */
- on_lift = !comp[comp_staylift]
- && target && target->health > 0
- && target->subsector->sector->tag==actor->subsector->sector->tag &&
- P_IsOnLift(actor);
- under_damage = monster_avoid_hazards && P_IsUnderDamage(actor);
- // killough 10/98: allow dogs to drop off of taller ledges sometimes.
- // dropoff==1 means always allow it, dropoff==2 means only up to 128 high,
- // and only if the target is immediately on the other side of the line.
- #ifdef DOGS
- // haleyjd: allow all friends of HelperType to also jump down
- if ((actor->type == MT_DOGS || (actor->type == (HelperThing-1) && actor->flags&MF_FRIEND))
- && target && dog_jumping &&
- !((target->flags ^ actor->flags) & MF_FRIEND) &&
- P_AproxDistance(actor->x - target->x,
- actor->y - target->y) < FRACUNIT*144 &&
- P_Random(pr_dropoff) < 235)
- dropoff = 2;
- #endif
- if (!P_Move(actor, dropoff))
- return false;
- // killough 9/9/98: avoid crushing ceilings or other damaging areas
- if (
- (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift
- !P_IsOnLift(actor))
- ||
- (monster_avoid_hazards && !under_damage && // Get away from damage
- (under_damage = P_IsUnderDamage(actor)) &&
- (under_damage < 0 || P_Random(pr_avoidcrush) < 200))
- )
- actor->movedir = DI_NODIR; // avoid the area (most of the time anyway)
- return true;
- }
- //
- // TryWalk
- // Attempts to move actor on
- // in its current (ob->moveangle) direction.
- // If blocked by either a wall or an actor
- // returns FALSE
- // If move is either clear or blocked only by a door,
- // returns TRUE and sets...
- // If a door is in the way,
- // an OpenDoor call is made to start it opening.
- //
- static boolean P_TryWalk(mobj_t *actor)
- {
- if (!P_SmartMove(actor))
- return false;
- actor->movecount = P_Random(pr_trywalk)&15;
- return true;
- }
- //
- // P_DoNewChaseDir
- //
- // killough 9/8/98:
- //
- // Most of P_NewChaseDir(), except for what
- // determines the new direction to take
- //
- static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay)
- {
- dirtype_t xdir, ydir, tdir;
- dirtype_t olddir = actor->movedir;
- dirtype_t turnaround = olddir;
- if (turnaround != DI_NODIR) // find reverse direction
- turnaround ^= 4;
- xdir =
- deltax > 10*FRACUNIT ? DI_EAST :
- deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR;
- ydir =
- deltay < -10*FRACUNIT ? DI_SOUTH :
- deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR;
- // try direct route
- if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround !=
- (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST :
- deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor))
- return;
- // try other directions
- if (P_Random(pr_newchase) > 200 || D_abs(deltay)>D_abs(deltax))
- tdir = xdir, xdir = ydir, ydir = tdir;
- if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR &&
- (actor->movedir = xdir, P_TryWalk(actor)))
- return; // either moved forward or attacked
- if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR &&
- (actor->movedir = ydir, P_TryWalk(actor)))
- return;
- // there is no direct path to the player, so pick another direction.
- if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor)))
- return;
- // randomly determine direction of search
- if (P_Random(pr_newchasedir) & 1)
- {
- for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
- if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor)))
- return;
- }
- else
- for (tdir = DI_SOUTHEAST; tdir != DI_EAST-1; tdir--)
- if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor)))
- return;
- if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor))
- actor->movedir = DI_NODIR;
- }
- //
- // killough 11/98:
- //
- // Monsters try to move away from tall dropoffs.
- //
- // In Doom, they were never allowed to hang over dropoffs,
- // and would remain stuck if involuntarily forced over one.
- // This logic, combined with p_map.c (P_TryMove), allows
- // monsters to free themselves without making them tend to
- // hang over dropoffs.
- static fixed_t dropoff_deltax, dropoff_deltay, floorz;
- static boolean PIT_AvoidDropoff(line_t *line)
- {
- if (line->backsector && // Ignore one-sided linedefs
- tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] &&
- tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] &&
- tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted
- tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] &&
- P_BoxOnLineSide(tmbbox, line) == -1)
- {
- fixed_t front = line->frontsector->floorheight;
- fixed_t back = line->backsector->floorheight;
- angle_t angle;
- // The monster must contact one of the two floors,
- // and the other must be a tall dropoff (more than 24).
- if (back == floorz && front < floorz - FRACUNIT*24)
- angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff
- else
- if (front == floorz && back < floorz - FRACUNIT*24)
- angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff
- else
- return true;
- // Move away from dropoff at a standard speed.
- // Multiple contacted linedefs are cumulative (e.g. hanging over corner)
- dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32;
- dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32;
- }
- return true;
- }
- //
- // Driver for above
- //
- static fixed_t P_AvoidDropoff(mobj_t *actor)
- {
- int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT;
- int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT;
- int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT;
- int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT;
- int bx, by;
- floorz = actor->z; // remember floor height
- dropoff_deltax = dropoff_deltay = 0;
- // check lines
- validcount++;
- for (bx=xl ; bx<=xh ; bx++)
- for (by=yl ; by<=yh ; by++)
- P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines
- return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed
- }
- //
- // P_NewChaseDir
- //
- // killough 9/8/98: Split into two functions
- //
- static void P_NewChaseDir(mobj_t *actor)
- {
- mobj_t *target = actor->target;
- fixed_t deltax = target->x - actor->x;
- fixed_t deltay = target->y - actor->y;
- // killough 8/8/98: sometimes move away from target, keeping distance
- //
- // 1) Stay a certain distance away from a friend, to avoid being in their way
- // 2) Take advantage over an enemy without missiles, by keeping distance
- actor->strafecount = 0;
- if (mbf_features) {
- if (actor->floorz - actor->dropoffz > FRACUNIT*24 &&
- actor->z <= actor->floorz &&
- !(actor->flags & (MF_DROPOFF|MF_FLOAT)) &&
- !comp[comp_dropoff] &&
- P_AvoidDropoff(actor)) /* Move away from dropoff */
- {
- P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay);
- // If moving away from dropoff, set movecount to 1 so that
- // small steps are taken to get monster away from dropoff.
- actor->movecount = 1;
- return;
- }
- else
- {
- fixed_t dist = P_AproxDistance(deltax, deltay);
- // Move away from friends when too close, except
- // in certain situations (e.g. a crowded lift)
- if (actor->flags & target->flags & MF_FRIEND &&
- distfriend << FRACBITS > dist &&
- !P_IsOnLift(target) && !P_IsUnderDamage(actor))
- {
- deltax = -deltax, deltay = -deltay;
- } else
- if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND)
- { // Live enemy target
- if (monster_backing &&
- actor->info->missilestate && actor->type != MT_SKULL &&
- ((!target->info->missilestate && dist < MELEERANGE*2) ||
- (target->player && dist < MELEERANGE*3 &&
- (target->player->readyweapon == wp_fist ||
- target->player->readyweapon == wp_chainsaw))))
- { // Back away from melee attacker
- actor->strafecount = P_Random(pr_enemystrafe) & 15;
- deltax = -deltax, deltay = -deltay;
- }
- }
- }
- }
- P_DoNewChaseDir(actor, deltax, deltay);
- // If strafing, set movecount to strafecount so that old Doom
- // logic still works the same, except in the strafing part
- if (actor->strafecount)
- actor->movecount = actor->strafecount;
- }
- //
- // P_IsVisible
- //
- // killough 9/9/98: whether a target is visible to a monster
- //
- static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround)
- {
- if (!allaround)
- {
- angle_t an = R_PointToAngle2(actor->x, actor->y,
- mo->x, mo->y) - actor->angle;
- if (an > ANG90 && an < ANG270 &&
- P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE)
- return false;
- }
- return P_CheckSight(actor, mo);
- }
- //
- // PIT_FindTarget
- //
- // killough 9/5/98
- //
- // Finds monster targets for other monsters
- //
- static int current_allaround;
- static boolean PIT_FindTarget(mobj_t *mo)
- {
- mobj_t *actor = current_actor;
- if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target
- mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL)))
- return true;
- // If the monster is already engaged in a one-on-one attack
- // with a healthy friend, don't attack around 60% the time
- {
- const mobj_t *targ = mo->target;
- if (targ && targ->target == mo &&
- P_Random(pr_skiptarget) > 100 &&
- (targ->flags ^ mo->flags) & MF_FRIEND &&
- targ->health*2 >= targ->info->spawnhealth)
- return true;
- }
- if (!P_IsVisible(actor, mo, current_allaround))
- return true;
- P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target
- P_SetTarget(&actor->target, mo); // Found target
- // Move the selected monster to the end of its associated
- // list, so that it gets searched last next time.
- {
- thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ?
- th_friends : th_enemies];
- (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev;
- (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker;
- (mo->thinker.cnext = cap)->cprev = &mo->thinker;
- }
- return false;
- }
- //
- // P_LookForPlayers
- // If allaround is false, only look 180 degrees in front.
- // Returns true if a player is targeted.
- //
- static boolean P_LookForPlayers(mobj_t *actor, boolean allaround)
- {
- player_t *player;
- int stop, stopc, c;
- if (actor->flags & MF_FRIEND)
- { // killough 9/9/98: friendly monsters go about players differently
- int anyone;
- #if 0
- if (!allaround) // If you want friendly monsters not to awaken unprovoked
- return false;
- #endif
- // Go back to a player, no matter whether it's visible or not
- for (anyone=0; anyone<=1; anyone++)
- for (c=0; c<MAXPLAYERS; c++)
- if (playeringame[c] && players[c].playerstate==PST_LIVE &&
- (anyone || P_IsVisible(actor, players[c].mo, allaround)))
- {
- P_SetTarget(&actor->target, players[c].mo);
- // killough 12/98:
- // get out of refiring loop, to avoid hitting player accidentally
- if (actor->info->missilestate)
- {
- P_SetMobjState(actor, actor->info->seestate);
- actor->flags &= ~MF_JUSTHIT;
- }
- return true;
- }
- return false;
- }
- // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98:
- stop = (actor->lastlook-1)&(MAXPLAYERS-1);
- c = 0;
- stopc = !mbf_features &&
- !demo_compatibility && monsters_remember ?
- MAXPLAYERS : 2; // killough 9/9/98
- for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1))
- {
- if (!playeringame[actor->lastlook])
- continue;
- // killough 2/15/98, 9/9/98:
- if (c++ == stopc || actor->lastlook == stop) // done looking
- {
- // e6y
- // Fixed Boom incompatibilities. The following code was missed.
- // There are no more desyncs on Donce's demos on horror.wad
- // Use last known enemy if no players sighted -- killough 2/15/98:
- if (!mbf_features && !demo_compatibility && monsters_remember)
- {
- if (actor->lastenemy && actor->lastenemy->health > 0)
- {
- actor->target = actor->lastenemy;
- actor->lastenemy = NULL;
- return true;
- }
- }
- return false;
- }
- player = &players[actor->lastlook];
- if (player->health <= 0)
- continue; // dead
- if (!P_IsVisible(actor, player->mo, allaround))
- continue;
- P_SetTarget(&actor->target, player->mo);
- /* killough 9/9/98: give monsters a threshold towards getting players
- * (we don't want it to be too easy for a player with dogs :)
- */
- if (!comp[comp_pursuit])
- actor->threshold = 60;
- return true;
- }
- }
- //
- // Friendly monsters, by Lee Killough 7/18/98
- //
- // Friendly monsters go after other monsters first, but
- // also return to owner if they cannot find any targets.
- // A marine's best friend :) killough 7/18/98, 9/98
- //
- static boolean P_LookForMonsters(mobj_t *actor, boolean allaround)
- {
- thinker_t *cap, *th;
- if (demo_compatibility)
- return false;
- if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember &&
- !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends
- {
- P_SetTarget(&actor->target, actor->lastenemy);
- P_SetTarget(&actor->lastenemy, NULL);
- return true;
- }
- /* Old demos do not support monster-seeking bots */
- if (!mbf_features)
- return false;
- // Search the threaded list corresponding to this object's potential targets
- cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends];
- // Search for new enemy
- if (cap->cnext != cap) // Empty list? bail out early
- {
- int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT;
- int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT;
- int d;
- current_actor = actor;
- current_allaround = allaround;
- // Search first in the immediate vicinity.
- if (!P_BlockThingsIterator(x, y, PIT_FindTarget))
- return true;
- for (d=1; d<5; d++)
- {
- int i = 1 - d;
- do
- if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) ||
- !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget))
- return true;
- while (++i < d);
- do
- if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) ||
- !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget))
- return true;
- while (--i + d >= 0);
- }
- { // Random number of monsters, to prevent patterns from forming
- int n = (P_Random(pr_friends) & 31) + 15;
- for (th = cap->cnext; th != cap; th = th->cnext)
- if (--n < 0)
- {
- // Only a subset of the monsters were searched. Move all of
- // the ones which were searched so far, to the end of the list.
- (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext;
- (cap->cprev = th->cprev)->cnext = cap;
- (th->cprev = cap)->cnext = th;
- break;
- }
- else
- if (!PIT_FindTarget((mobj_t *) th)) // If target sighted
- return true;
- }
- }
- return false; // No monster found
- }
- //
- // P_LookForTargets
- //
- // killough 9/5/98: look for targets to go after, depending on kind of monster
- //
- static boolean P_LookForTargets(mobj_t *actor, int allaround)
- {
- return actor->flags & MF_FRIEND ?
- P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround):
- P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround);
- }
- //
- // P_HelpFriend
- //
- // killough 9/8/98: Help friends in danger of dying
- //
- static boolean P_HelpFriend(mobj_t *actor)
- {
- thinker_t *cap, *th;
- // If less than 33% health, self-preservation rules
- if (actor->health*3 < actor->info->spawnhealth)
- return false;
- current_actor = actor;
- current_allaround = true;
- // Possibly help a friend under 50% health
- cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies];
- for (th = cap->cnext; th != cap; th = th->cnext)
- if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth)
- {
- if (P_Random(pr_helpfriend) < 180)
- break;
- }
- else
- if (((mobj_t *) th)->flags & MF_JUSTHIT &&
- ((mobj_t *) th)->target &&
- ((mobj_t *) th)->target != actor->target &&
- !PIT_FindTarget(((mobj_t *) th)->target))
- {
- // Ignore any attacking monsters, while searching for friend
- actor->threshold = BASETHRESHOLD;
- return true;
- }
- return false;
- }
- //
- // A_KeenDie
- // DOOM II special, map 32.
- // Uses special tag 666.
- //
- void A_KeenDie(mobj_t* mo)
- {
- thinker_t *th;
- line_t junk;
- A_Fall(mo);
- // scan the remaining thinkers to see if all Keens are dead
- for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
- if (th->function == P_MobjThinker)
- {
- mobj_t *mo2 = (mobj_t *) th;
- if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
- return; // other Keen not dead
- }
- junk.tag = 666;
- EV_DoDoor(&junk,open);
- }
- //
- // ACTION ROUTINES
- //
- //
- // A_Look
- // Stay in state until a player is sighted.
- //
- void A_Look(mobj_t *actor)
- {
- mobj_t *targ = actor->subsector->sector->soundtarget;
- actor->threshold = 0; // any shot will wake up
- /* killough 7/18/98:
- * Friendly monsters go after other monsters first, but
- * also return to player, without attacking them, if they
- * cannot find any targets. A marine's best friend :)
- */
- actor->pursuecount = 0;
- if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) &&
- !((targ = actor->subsector->sector->soundtarget) &&
- targ->flags & MF_SHOOTABLE &&
- (P_SetTarget(&actor->target, targ),
- !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) &&
- (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false)))
- return;
- // go into chase state
- if (actor->info->seesound)
- {
- int sound;
- switch (actor->info->seesound)
- {
- case sfx_posit1:
- case sfx_posit2:
- case sfx_posit3:
- sound = sfx_posit1+P_Random(pr_see)%3;
- break;
- case sfx_bgsit1:
- case sfx_bgsit2:
- sound = sfx_bgsit1+P_Random(pr_see)%2;
- break;
- default:
- sound = actor->info->seesound;
- break;
- }
- if (actor->type==MT_SPIDER || actor->type == MT_CYBORG)
- S_StartSound(NULL, sound); // full volume
- else
- S_StartSound(actor, sound);
- }
- P_SetMobjState(actor, actor->info->seestate);
- }
- //
- // A_KeepChasing
- //
- // killough 10/98:
- // Allows monsters to continue movement while attacking
- //
- static void A_KeepChasing(mobj_t *actor)
- {
- if (actor->movecount)
- {
- actor->movecount--;
- if (actor->strafecount)
- actor->strafecount--;
- P_SmartMove(actor);
- }
- }
- //
- // A_Chase
- // Actor has a melee attack,
- // so it tries to close as fast as possible
- //
- void A_Chase(mobj_t *actor)
- {
- if (actor->reactiontime)
- actor->reactiontime--;
- if (actor->threshold) { /* modify target threshold */
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
- /* turn towards movement direction if not there yet
- * killough 9/7/98: keep facing towards target if strafing or backing out
- */
- if (actor->strafecount)
- A_FaceTarget(actor);
- else if (actor->movedir < 8)
- {
- int delta = (actor->angle &= (7<<29)) - (actor->movedir << 29);
- if (delta > 0)
- actor->angle -= ANG90/2;
- else
- if (delta < 0)
- actor->angle += ANG90/2;
- }
- if (!actor->target || !(actor->target->flags&MF_SHOOTABLE))
- {
- if (!P_LookForTargets(actor,true)) // look for a new target
- P_SetMobjState(actor, actor->info->spawnstate); // no new target
- return;
- }
- // do not attack twice in a row
- if (actor->flags & MF_JUSTATTACKED)
- {
- actor->flags &= ~MF_JUSTATTACKED;
- if (gameskill != sk_nightmare && !fastparm)
- P_NewChaseDir(actor);
- return;
- }
- // check for melee attack
- if (actor->info->meleestate && P_CheckMeleeRange(actor))
- {
- if (actor->info->attacksound)
- S_StartSound(actor, actor->info->attacksound);
- P_SetMobjState(actor, actor->info->meleestate);
- /* killough 8/98: remember an attack
- * cph - DEMOSYNC? */
- if (!actor->info->missilestate)
- actor->flags |= MF_JUSTHIT;
- return;
- }
- // check for missile attack
- if (actor->info->missilestate)
- if (!(gameskill < sk_nightmare && !fastparm && actor->movecount))
- if (P_CheckMissileRange(actor))
- {
- P_SetMobjState(actor, actor->info->missilestate);
- actor->flags |= MF_JUSTATTACKED;
- return;
- }
- if (!actor->threshold) {
- if (!mbf_features)
- { /* killough 9/9/98: for backward demo compatibility */
- if (netgame && !P_CheckSight(actor, actor->target) &&
- P_LookForPlayers(actor, true))
- return;
- }
- /* killough 7/18/98, 9/9/98: new monster AI */
- else if (help_friends && P_HelpFriend(actor))
- return; /* killough 9/8/98: Help friends in need */
- /* Look for new targets if current one is bad or is out of view */
- else if (actor->pursuecount)
- actor->pursuecount--;
- else {
- /* Our pursuit time has expired. We're going to think about
- * changing targets */
- actor->pursuecount = BASETHRESHOLD;
- /* Unless (we have a live target
- * and it's not friendly
- * and we can see it)
- * try to find a new one; return if sucessful */
- if (!(actor->target && actor->target->health > 0 &&
- ((comp[comp_pursuit] && !netgame) ||
- (((actor->target->flags ^ actor->flags) & MF_FRIEND ||
- (!(actor->flags & MF_FRIEND) && monster_infighting)) &&
- P_CheckSight(actor, actor->target))))
- && P_LookForTargets(actor, true))
- return;
- /* (Current target was good, or no new target was found.)
- *
- * If monster is a missile-less friend, give up pursuit and
- * return to player, if no attacks have occurred recently.
- */
- if (!actor->info->missilestate && actor->flags & MF_FRIEND) {
- if (actor->flags & MF_JUSTHIT) /* if recent action, */
- actor->flags &= ~MF_JUSTHIT; /* keep fighting */
- else if (P_LookForPlayers(actor, true)) /* else return to player */
- return;
- }
- }
- }
- if (actor->strafecount)
- actor->strafecount--;
- // chase towards player
- if (--actor->movecount<0 || !P_SmartMove(actor))
- P_NewChaseDir(actor);
- // make active sound
- if (actor->info->activesound && P_Random(pr_see)<3)
- S_StartSound(actor, actor->info->activesound);
- }
- //
- // A_FaceTarget
- //
- void A_FaceTarget(mobj_t *actor)
- {
- if (!actor->target)
- return;
- actor->flags &= ~MF_AMBUSH;
- actor->angle = R_PointToAngle2(actor->x, actor->y,
- actor->target->x, actor->target->y);
- if (actor->target->flags & MF_SHADOW)
- { // killough 5/5/98: remove dependence on order of evaluation:
- int t = P_Random(pr_facetarget);
- actor->angle += (t-P_Random(pr_facetarget))<<21;
- }
- }
- //
- // A_PosAttack
- //
- void A_PosAttack(mobj_t *actor)
- {
- int angle, damage, slope, t;
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- angle = actor->angle;
- slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */
- S_StartSound(actor, sfx_pistol);
- // killough 5/5/98: remove dependence on order of evaluation:
- t = P_Random(pr_posattack);
- angle += (t - P_Random(pr_posattack))<<20;
- damage = (P_Random(pr_posattack)%5 + 1)*3;
- P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
- }
- void A_SPosAttack(mobj_t* actor)
- {
- int i, bangle, slope;
- if (!actor->target)
- return;
- S_StartSound(actor, sfx_shotgn);
- A_FaceTarget(actor);
- bangle = actor->angle;
- slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */
- for (i=0; i<3; i++)
- { // killough 5/5/98: remove dependence on order of evaluation:
- int t = P_Random(pr_sposattack);
- int angle = bangle + ((t - P_Random(pr_sposattack))<<20);
- int damage = ((P_Random(pr_sposattack)%5)+1)*3;
- P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
- }
- }
- void A_CPosAttack(mobj_t *actor)
- {
- int angle, bangle, damage, slope, t;
- if (!actor->target)
- return;
- S_StartSound(actor, sfx_shotgn);
- A_FaceTarget(actor);
- bangle = actor->angle;
- slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */
- // killough 5/5/98: remove dependence on order of evaluation:
- t = P_Random(pr_cposattack);
- angle = bangle + ((t - P_Random(pr_cposattack))<<20);
- damage = ((P_Random(pr_cposattack)%5)+1)*3;
- P_LineAttack(actor, angle, MISSILERANGE, slope, damage);
- }
- void A_CPosRefire(mobj_t *actor)
- {
- // keep firing unless target got out of sight
- A_FaceTarget(actor);
- /* killough 12/98: Stop firing if a friend has gotten in the way */
- if (P_HitFriend(actor))
- goto stop;
- /* killough 11/98: prevent refiring on friends continuously */
- if (P_Random(pr_cposrefire) < 40) {
- if (actor->target && actor->flags & actor->target->flags & MF_FRIEND)
- goto stop;
- else
- return;
- }
- if (!actor->target || actor->target->health <= 0
- || !P_CheckSight(actor, actor->target))
- stop: P_SetMobjState(actor, actor->info->seestate);
- }
- void A_SpidRefire(mobj_t* actor)
- {
- // keep firing unless target got out of sight
- A_FaceTarget(actor);
- /* killough 12/98: Stop firing if a friend has gotten in the way */
- if (P_HitFriend(actor))
- goto stop;
- if (P_Random(pr_spidrefire) < 10)
- return;
- // killough 11/98: prevent refiring on friends continuously
- if (!actor->target || actor->target->health <= 0
- || actor->flags & actor->target->flags & MF_FRIEND
- || !P_CheckSight(actor, actor->target))
- stop: P_SetMobjState(actor, actor->info->seestate);
- }
- void A_BspiAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile
- }
- //
- // A_TroopAttack
- //
- void A_TroopAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- if (P_CheckMeleeRange(actor))
- {
- int damage;
- S_StartSound(actor, sfx_claw);
- damage = (P_Random(pr_troopattack)%8+1)*3;
- P_DamageMobj(actor->target, actor, actor, damage);
- return;
- }
- P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile
- }
- void A_SargAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- if (P_CheckMeleeRange(actor))
- {
- int damage = ((P_Random(pr_sargattack)%10)+1)*4;
- P_DamageMobj(actor->target, actor, actor, damage);
- }
- }
- void A_HeadAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget (actor);
- if (P_CheckMeleeRange(actor))
- {
- int damage = (P_Random(pr_headattack)%6+1)*10;
- P_DamageMobj(actor->target, actor, actor, damage);
- return;
- }
- P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile
- }
- void A_CyberAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- P_SpawnMissile(actor, actor->target, MT_ROCKET);
- }
- void A_BruisAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- if (P_CheckMeleeRange(actor))
- {
- int damage;
- S_StartSound(actor, sfx_claw);
- damage = (P_Random(pr_bruisattack)%8+1)*10;
- P_DamageMobj(actor->target, actor, actor, damage);
- return;
- }
- P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile
- }
- //
- // A_SkelMissile
- //
- void A_SkelMissile(mobj_t *actor)
- {
- mobj_t *mo;
- if (!actor->target)
- return;
- A_FaceTarget (actor);
- actor->z += 16*FRACUNIT; // so missile spawns higher
- mo = P_SpawnMissile (actor, actor->target, MT_TRACER);
- actor->z -= 16*FRACUNIT; // back to normal
- mo->x += mo->momx;
- mo->y += mo->momy;
- P_SetTarget(&mo->tracer, actor->target);
- }
- int TRACEANGLE = 0xc000000;
- void A_Tracer(mobj_t *actor)
- {
- angle_t exact;
- fixed_t dist;
- fixed_t slope;
- mobj_t *dest;
- mobj_t *th;
- /* killough 1/18/98: this is why some missiles do not have smoke
- * and some do. Also, internal demos start at random gametics, thus
- * the bug in which revenants cause internal demos to go out of sync.
- *
- * killough 3/6/98: fix revenant internal demo bug by subtracting
- * levelstarttic from gametic.
- *
- * killough 9/29/98: use new "basetic" so that demos stay in sync
- * during pauses and menu activations, while retaining old demo sync.
- *
- * leveltime would have been better to use to start with in Doom, but
- * since old demos were recorded using gametic, we must stick with it,
- * and improvise around it (using leveltime causes desync across levels).
- */
- if ((gametic-basetic) & 3)
- return;
- // spawn a puff of smoke behind the rocket
- P_SpawnPuff(actor->x, actor->y, actor->z);
- th = P_SpawnMobj (actor->x-actor->momx,
- actor->y-actor->momy,
- actor->z, MT_SMOKE);
- th->momz = FRACUNIT;
- th->tics -= P_Random(pr_tracer) & 3;
- if (th->tics < 1)
- th->tics = 1;
- // adjust direction
- dest = actor->tracer;
- if (!dest || dest->health <= 0)
- return;
- // change angle
- exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
- if (exact != actor->angle) {
- if (exact - actor->angle > 0x80000000)
- {
- actor->angle -= TRACEANGLE;
- if (exact - actor->angle < 0x80000000)
- actor->angle = exact;
- }
- else
- {
- actor->angle += TRACEANGLE;
- if (exact - actor->angle > 0x80000000)
- actor->angle = exact;
- }
- }
- exact = actor->angle>>ANGLETOFINESHIFT;
- actor->momx = FixedMul(actor->info->speed, finecosine[exact]);
- actor->momy = FixedMul(actor->info->speed, finesine[exact]);
- // change slope
- dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
- dist = dist / actor->info->speed;
- if (dist < 1)
- dist = 1;
- slope = (dest->z+40*FRACUNIT - actor->z) / dist;
- if (slope < actor->momz)
- actor->momz -= FRACUNIT/8;
- else
- actor->momz += FRACUNIT/8;
- }
- void A_SkelWhoosh(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- S_StartSound(actor,sfx_skeswg);
- }
- void A_SkelFist(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- if (P_CheckMeleeRange(actor))
- {
- int damage = ((P_Random(pr_skelfist)%10)+1)*6;
- S_StartSound(actor, sfx_skepch);
- P_DamageMobj(actor->target, actor, actor, damage);
- }
- }
- //
- // PIT_VileCheck
- // Detect a corpse that could be raised.
- //
- mobj_t* corpsehit;
- mobj_t* vileobj;
- fixed_t viletryx;
- fixed_t viletryy;
- static boolean PIT_VileCheck(mobj_t *thing)
- {
- int maxdist;
- boolean check;
- if (!(thing->flags & MF_CORPSE) )
- return true; // not a monster
- if (thing->tics != -1)
- return true; // not lying still yet
- if (thing->info->raisestate == S_NULL)
- return true; // monster doesn't have a raise state
- maxdist = thing->info->radius + mobjinfo[MT_VILE].radius;
- if (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist)
- return true; // not actually touching
- // Check to see if the radius and height are zero. If they are // phares
- // then this is a crushed monster that has been turned into a // |
- // gib. One of the options may be to ignore this guy. // V
- // Option 1: the original, buggy method, -> ghost (compatibility)
- // Option 2: ressurect the monster, but not as a ghost
- // Option 3: ignore the gib
- // if (Option3) // ^
- // if ((thing->height == 0) && (thing->radius == 0)) // |
- // return true; // phares
- corpsehit = thing;
- corpsehit->momx = corpsehit->momy = 0;
- if (comp[comp_vile]) // phares
- { // |
- corpsehit->height <<= 2; // V
- check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y);
- corpsehit->height >>= 2;
- }
- else
- {
- int height,radius;
- height = corpsehit->height; // save temporarily
- radius = corpsehit->radius; // save temporarily
- corpsehit->height = corpsehit->info->height;
- corpsehit->radius = corpsehit->info->radius;
- corpsehit->flags |= MF_SOLID;
- check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y);
- corpsehit->height = height; // restore
- corpsehit->radius = radius; // restore // ^
- corpsehit->flags &= ~MF_SOLID;
- } // |
- // phares
- if (!check)
- return true; // doesn't fit here
- return false; // got one, so stop checking
- }
- //
- // A_VileChase
- // Check for ressurecting a body
- //
- void A_VileChase(mobj_t* actor)
- {
- int xl, xh;
- int yl, yh;
- int bx, by;
- if (actor->movedir != DI_NODIR)
- {
- // check for corpses to raise
- viletryx =
- actor->x + actor->info->speed*xspeed[actor->movedir];
- viletryy =
- actor->y + actor->info->speed*yspeed[actor->movedir];
- xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT;
- xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT;
- yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT;
- yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT;
- vileobj = actor;
- for (bx=xl ; bx<=xh ; bx++)
- {
- for (by=yl ; by<=yh ; by++)
- {
- // Call PIT_VileCheck to check
- // whether object is a corpse
- // that canbe raised.
- if (!P_BlockThingsIterator(bx,by,PIT_VileCheck))
- {
- mobjinfo_t *info;
- // got one!
- mobj_t* temp = actor->target;
- actor->target = corpsehit;
- A_FaceTarget(actor);
- actor->target = temp;
- P_SetMobjState(actor, S_VILE_HEAL1);
- S_StartSound(corpsehit, sfx_slop);
- info = corpsehit->info;
- P_SetMobjState(corpsehit,info->raisestate);
- if (comp[comp_vile]) // phares
- corpsehit->height <<= 2; // |
- else // V
- {
- corpsehit->height = info->height; // fix Ghost bug
- corpsehit->radius = info->radius; // fix Ghost bug
- } // phares
- /* killough 7/18/98:
- * friendliness is transferred from AV to raised corpse
- */
- corpsehit->flags =
- (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND);
- if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL)))
- totallive++;
-
- corpsehit->health = info->spawnhealth;
- P_SetTarget(&corpsehit->target, NULL); // killough 11/98
- if (mbf_features)
- { /* kilough 9/9/98 */
- P_SetTarget(&corpsehit->lastenemy, NULL);
- corpsehit->flags &= ~MF_JUSTHIT;
- }
- /* killough 8/29/98: add to appropriate thread */
- P_UpdateThinker(&corpsehit->thinker);
- return;
- }
- }
- }
- }
- A_Chase(actor); // Return to normal attack.
- }
- //
- // A_VileStart
- //
- void A_VileStart(mobj_t *actor)
- {
- S_StartSound(actor, sfx_vilatk);
- }
- //
- // A_Fire
- // Keep fire in front of player unless out of sight
- //
- void A_StartFire(mobj_t *actor)
- {
- S_StartSound(actor,sfx_flamst);
- A_Fire(actor);
- }
- void A_FireCrackle(mobj_t* actor)
- {
- S_StartSound(actor,sfx_flame);
- A_Fire(actor);
- }
- void A_Fire(mobj_t *actor)
- {
- unsigned an;
- mobj_t *dest = actor->tracer;
- if (!dest)
- return;
- // don't move it if the vile lost sight
- if (!P_CheckSight(actor->target, dest) )
- return;
- an = dest->angle >> ANGLETOFINESHIFT;
- P_UnsetThingPosition(actor);
- actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]);
- actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]);
- actor->z = dest->z;
- P_SetThingPosition(actor);
- }
- //
- // A_VileTarget
- // Spawn the hellfire
- //
- void A_VileTarget(mobj_t *actor)
- {
- mobj_t *fog;
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned
- fog = P_SpawnMobj(actor->target->x,
- (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y,
- actor->target->z,MT_FIRE);
- P_SetTarget(&actor->tracer, fog);
- P_SetTarget(&fog->target, actor);
- P_SetTarget(&fog->tracer, actor->target);
- A_Fire(fog);
- }
- //
- // A_VileAttack
- //
- void A_VileAttack(mobj_t *actor)
- {
- mobj_t *fire;
- int an;
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- if (!P_CheckSight(actor, actor->target))
- return;
- S_StartSound(actor, sfx_barexp);
- P_DamageMobj(actor->target, actor, actor, 20);
- actor->target->momz = 1000*FRACUNIT/actor->target->info->mass;
- an = actor->angle >> ANGLETOFINESHIFT;
- fire = actor->tracer;
- if (!fire)
- return;
- // move the fire between the vile and the player
- fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
- fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
- P_RadiusAttack(fire, actor, 70);
- }
- //
- // Mancubus attack,
- // firing three missiles (bruisers)
- // in three different directions?
- // Doesn't look like it.
- //
- #define FATSPREAD (ANG90/8)
- void A_FatRaise(mobj_t *actor)
- {
- A_FaceTarget(actor);
- S_StartSound(actor, sfx_manatk);
- }
- void A_FatAttack1(mobj_t *actor)
- {
- mobj_t *mo;
- int an;
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- // Change direction to ...
- actor->angle += FATSPREAD;
- P_SpawnMissile(actor, actor->target, MT_FATSHOT);
- mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT);
- mo->angle += FATSPREAD;
- an = mo->angle >> ANGLETOFINESHIFT;
- mo->momx = FixedMul(mo->info->speed, finecosine[an]);
- mo->momy = FixedMul(mo->info->speed, finesine[an]);
- }
- void A_FatAttack2(mobj_t *actor)
- {
- mobj_t *mo;
- int an;
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- // Now here choose opposite deviation.
- actor->angle -= FATSPREAD;
- P_SpawnMissile(actor, actor->target, MT_FATSHOT);
- mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
- mo->angle -= FATSPREAD*2;
- an = mo->angle >> ANGLETOFINESHIFT;
- mo->momx = FixedMul(mo->info->speed, finecosine[an]);
- mo->momy = FixedMul(mo->info->speed, finesine[an]);
- }
- void A_FatAttack3(mobj_t *actor)
- {
- mobj_t *mo;
- int an;
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
- mo->angle -= FATSPREAD/2;
- an = mo->angle >> ANGLETOFINESHIFT;
- mo->momx = FixedMul(mo->info->speed, finecosine[an]);
- mo->momy = FixedMul(mo->info->speed, finesine[an]);
- mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT);
- mo->angle += FATSPREAD/2;
- an = mo->angle >> ANGLETOFINESHIFT;
- mo->momx = FixedMul(mo->info->speed, finecosine[an]);
- mo->momy = FixedMul(mo->info->speed, finesine[an]);
- }
- //
- // SkullAttack
- // Fly at the player like a missile.
- //
- #define SKULLSPEED (20*FRACUNIT)
- void A_SkullAttack(mobj_t *actor)
- {
- mobj_t *dest;
- angle_t an;
- int dist;
- if (!actor->target)
- return;
- dest = actor->target;
- actor->flags |= MF_SKULLFLY;
- S_StartSound(actor, actor->info->attacksound);
- A_FaceTarget(actor);
- an = actor->angle >> ANGLETOFINESHIFT;
- actor->momx = FixedMul(SKULLSPEED, finecosine[an]);
- actor->momy = FixedMul(SKULLSPEED, finesine[an]);
- dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
- dist = dist / SKULLSPEED;
- if (dist < 1)
- dist = 1;
- actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist;
- }
- //
- // A_PainShootSkull
- // Spawn a lost soul and launch it at the target
- //
- static void A_PainShootSkull(mobj_t *actor, angle_t angle)
- {
- fixed_t x,y,z;
- mobj_t *newmobj;
- angle_t an;
- int prestep;
- // The original code checked for 20 skulls on the level, // phares
- // and wouldn't spit another one if there were. If not in // phares
- // compatibility mode, we remove the limit. // phares
- // phares
- if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */
- {
- // count total number of skulls currently on the level
- int count = 0;
- thinker_t *currentthinker = NULL;
- while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL)
- if ((currentthinker->function == P_MobjThinker)
- && ((mobj_t *)currentthinker)->type == MT_SKULL)
- count++;
- if (count > 20) // phares
- return; // phares
- }
- // okay, there's room for another one
- an = angle >> ANGLETOFINESHIFT;
- prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2;
- x = actor->x + FixedMul(prestep, finecosine[an]);
- y = actor->y + FixedMul(prestep, finesine[an]);
- z = actor->z + 8*FRACUNIT;
- if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */
- newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares
- else // V
- {
- // Check whether the Lost Soul is being fired through a 1-sided
- // wall or an impassible line, or a "monsters can't cross" line.
- // If it is, then we don't allow the spawn. This is a bug fix, but
- // it should be considered an enhancement, since it may disturb
- // existing demos, so don't do it in compatibility mode.
- if (Check_Sides(actor,x,y))
- return;
- newmobj = P_SpawnMobj(x, y, z, MT_SKULL);
- // Check to see if the new Lost Soul's z value is above the
- // ceiling of its new sector, or below the floor. If so, kill it.
- if ((newmobj->z >
- (newmobj->subsector->sector->ceilingheight - newmobj->height)) ||
- (newmobj->z < newmobj->subsector->sector->floorheight))
- {
- // kill it immediately
- P_DamageMobj(newmobj,actor,actor,10000);
- return; // ^
- } // |
- } // phares
- /* killough 7/20/98: PEs shoot lost souls with the same friendliness */
- newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND);
- /* killough 8/29/98: add to appropriate thread */
- P_UpdateThinker(&newmobj->thinker);
- // Check for movements.
- // killough 3/15/98: don't jump over dropoffs:
- if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false))
- {
- // kill it immediately
- P_DamageMobj(newmobj, actor, actor, 10000);
- return;
- }
- P_SetTarget(&newmobj->target, actor->target);
- A_SkullAttack(newmobj);
- }
- //
- // A_PainAttack
- // Spawn a lost soul and launch it at the target
- //
- void A_PainAttack(mobj_t *actor)
- {
- if (!actor->target)
- return;
- A_FaceTarget(actor);
- A_PainShootSkull(actor, actor->angle);
- }
- void A_PainDie(mobj_t *actor)
- {
- A_Fall(actor);
- A_PainShootSkull(actor, actor->angle+ANG90);
- A_PainShootSkull(actor, actor->angle+ANG180);
- A_PainShootSkull(actor, actor->angle+ANG270);
- }
- void A_Scream(mobj_t *actor)
- {
- int sound;
- switch (actor->info->deathsound)
- {
- case 0:
- return;
- case sfx_podth1:
- case sfx_podth2:
- case sfx_podth3:
- sound = sfx_podth1 + P_Random(pr_scream)%3;
- break;
- case sfx_bgdth1:
- case sfx_bgdth2:
- sound = sfx_bgdth1 + P_Random(pr_scream)%2;
- break;
- default:
- sound = actor->info->deathsound;
- break;
- }
- // Check for bosses.
- if (actor->type==MT_SPIDER || actor->type == MT_CYBORG)
- S_StartSound(NULL, sound); // full volume
- else
- S_StartSound(actor, sound);
- }
- void A_XScream(mobj_t *actor)
- {
- S_StartSound(actor, sfx_slop);
- }
- void A_Pain(mobj_t *actor)
- {
- if (actor->info->painsound)
- S_StartSound(actor, actor->info->painsound);
- }
- void A_Fall(mobj_t *actor)
- {
- // actor is on ground, it can be walked over
- actor->flags &= ~MF_SOLID;
- }
- //
- // A_Explode
- //
- void A_Explode(mobj_t *thingy)
- {
- P_RadiusAttack( thingy, thingy->target, 128 );
- }
- //
- // A_BossDeath
- // Possibly trigger special effects
- // if on first boss level
- //
- void A_BossDeath(mobj_t *mo)
- {
- thinker_t *th;
- line_t junk;
- int i;
- if (gamemode == commercial)
- {
- if (gamemap != 7)
- return;
- if ((mo->type != MT_FATSO)
- && (mo->type != MT_BABY))
- return;
- }
- else
- {
- // e6y
- // Additional check of gameepisode is necessary, because
- // there is no right or wrong solution for E4M6 in original EXEs,
- // there's nothing to emulate.
- if (comp[comp_666] && gameepisode < 4)
- {
- // e6y
- // Only following checks are present in doom2.exe ver. 1.666 and 1.9
- // instead of separate checks for each episode in doomult.exe, plutonia.exe and tnt.exe
- // There is no more desync on doom.wad\episode3.lmp
- // http://www.doomworld.com/idgames/index.php?id=6909
- if (gamemap != 8)
- return;
- if (mo->type == MT_BRUISER && gameepisode != 1)
- return;
- }
- else
- {
- switch(gameepisode)
- {
- case 1:
- if (gamemap != 8)
- return;
- if (mo->type != MT_BRUISER)
- return;
- break;
- case 2:
- if (gamemap != 8)
- return;
- if (mo->type != MT_CYBORG)
- return;
- break;
- case 3:
- if (gamemap != 8)
- return;
- if (mo->type != MT_SPIDER)
- return;
- break;
- case 4:
- switch(gamemap)
- {
- case 6:
- if (mo->type != MT_CYBORG)
- return;
- break;
- case 8:
- if (mo->type != MT_SPIDER)
- return;
- break;
- default:
- return;
- break;
- }
- break;
- default:
- if (gamemap != 8)
- return;
- break;
- }
- }
- }
- // make sure there is a player alive for victory
- for (i=0; i<MAXPLAYERS; i++)
- if (playeringame[i] && players[i].health > 0)
- break;
- if (i==MAXPLAYERS)
- return; // no one left alive, so do not end game
- // scan the remaining thinkers to see
- // if all bosses are dead
- for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
- if (th->function == P_MobjThinker)
- {
- mobj_t *mo2 = (mobj_t *) th;
- if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
- return; // other boss not dead
- }
- // victory!
- if ( gamemode == commercial)
- {
- if (gamemap == 7)
- {
- if (mo->type == MT_FATSO)
- {
- junk.tag = 666;
- EV_DoFloor(&junk,lowerFloorToLowest);
- return;
- }
- if (mo->type == MT_BABY)
- {
- junk.tag = 667;
- EV_DoFloor(&junk,raiseToTexture);
- return;
- }
- }
- }
- else
- {
- switch(gameepisode)
- {
- case 1:
- junk.tag = 666;
- EV_DoFloor(&junk, lowerFloorToLowest);
- return;
- break;
- case 4:
- switch(gamemap)
- {
- case 6:
- junk.tag = 666;
- EV_DoDoor(&junk, blazeOpen);
- return;
- break;
- case 8:
- junk.tag = 666;
- EV_DoFloor(&junk, lowerFloorToLowest);
- return;
- break;
- }
- }
- }
- G_ExitLevel();
- }
- void A_Hoof (mobj_t* mo)
- {
- S_StartSound(mo, sfx_hoof);
- A_Chase(mo);
- }
- void A_Metal(mobj_t *mo)
- {
- S_StartSound(mo, sfx_metal);
- A_Chase(mo);
- }
- void A_BabyMetal(mobj_t *mo)
- {
- S_StartSound(mo, sfx_bspwlk);
- A_Chase(mo);
- }
- void A_OpenShotgun2(player_t *player, pspdef_t *psp)
- {
- S_StartSound(player->mo, sfx_dbopn);
- }
- void A_LoadShotgun2(player_t *player, pspdef_t *psp)
- {
- S_StartSound(player->mo, sfx_dbload);
- }
- void A_CloseShotgun2(player_t *player, pspdef_t *psp)
- {
- S_StartSound(player->mo, sfx_dbcls);
- A_ReFire(player,psp);
- }
- // killough 2/7/98: Remove limit on icon landings:
- mobj_t **braintargets;
- int numbraintargets_alloc;
- int numbraintargets;
- struct brain_s brain; // killough 3/26/98: global state of boss brain
- // killough 3/26/98: initialize icon landings at level startup,
- // rather than at boss wakeup, to prevent savegame-related crashes
- void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function
- {
- thinker_t *thinker;
- // find all the target spots
- numbraintargets = 0;
- brain.targeton = 0;
- brain.easy = 0; // killough 3/26/98: always init easy to 0
- for (thinker = thinkercap.next ;
- thinker != &thinkercap ;
- thinker = thinker->next)
- if (thinker->function == P_MobjThinker)
- {
- mobj_t *m = (mobj_t *) thinker;
- if (m->type == MT_BOSSTARGET )
- { // killough 2/7/98: remove limit on icon landings:
- if (numbraintargets >= numbraintargets_alloc)
- braintargets = realloc(braintargets,
- (numbraintargets_alloc = numbraintargets_alloc ?
- numbraintargets_alloc*2 : 32) *sizeof *braintargets);
- braintargets[numbraintargets++] = m;
- }
- }
- }
- void A_BrainAwake(mobj_t *mo)
- {
- S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now
- }
- void A_BrainPain(mobj_t *mo)
- {
- S_StartSound(NULL,sfx_bospn);
- }
- void A_BrainScream(mobj_t *mo)
- {
- int x;
- for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)
- {
- int y = mo->y - 320*FRACUNIT;
- int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT;
- mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET);
- th->momz = P_Random(pr_brainscream)*512;
- P_SetMobjState(th, S_BRAINEXPLODE1);
- th->tics -= P_Random(pr_brainscream)&7;
- if (th->tics < 1)
- th->tics = 1;
- }
- S_StartSound(NULL,sfx_bosdth);
- }
- void A_BrainExplode(mobj_t *mo)
- { // killough 5/5/98: remove dependence on order of evaluation:
- int t = P_Random(pr_brainexp);
- int x = mo->x + (t - P_Random(pr_brainexp))*2048;
- int y = mo->y;
- int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT;
- mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET);
- th->momz = P_Random(pr_brainexp)*512;
- P_SetMobjState(th, S_BRAINEXPLODE1);
- th->tics -= P_Random(pr_brainexp)&7;
- if (th->tics < 1)
- th->tics = 1;
- }
- void A_BrainDie(mobj_t *mo)
- {
- G_ExitLevel();
- }
- void A_BrainSpit(mobj_t *mo)
- {
- mobj_t *targ, *newmobj;
- if (!numbraintargets) // killough 4/1/98: ignore if no targets
- return;
- brain.easy ^= 1; // killough 3/26/98: use brain struct
- if (gameskill <= sk_easy && !brain.easy)
- return;
- // shoot a cube at current target
- targ = braintargets[brain.targeton++]; // killough 3/26/98:
- brain.targeton %= numbraintargets; // Use brain struct for targets
- // spawn brain missile
- newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT);
- P_SetTarget(&newmobj->target, targ);
- newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics);
- // killough 7/18/98: brain friendliness is transferred
- newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
- // killough 8/29/98: add to appropriate thread
- P_UpdateThinker(&newmobj->thinker);
- S_StartSound(NULL, sfx_bospit);
- }
- // travelling cube sound
- void A_SpawnSound(mobj_t *mo)
- {
- S_StartSound(mo,sfx_boscub);
- A_SpawnFly(mo);
- }
- void A_SpawnFly(mobj_t *mo)
- {
- mobj_t *newmobj;
- mobj_t *fog;
- mobj_t *targ;
- int r;
- mobjtype_t type;
- if (--mo->reactiontime)
- return; // still flying
- targ = mo->target;
- // First spawn teleport fog.
- fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE);
- S_StartSound(fog, sfx_telept);
- // Randomly select monster to spawn.
- r = P_Random(pr_spawnfly);
- // Probability distribution (kind of :), decreasing likelihood.
- if ( r<50 )
- type = MT_TROOP;
- else if (r<90)
- type = MT_SERGEANT;
- else if (r<120)
- type = MT_SHADOWS;
- else if (r<130)
- type = MT_PAIN;
- else if (r<160)
- type = MT_HEAD;
- else if (r<162)
- type = MT_VILE;
- else if (r<172)
- type = MT_UNDEAD;
- else if (r<192)
- type = MT_BABY;
- else if (r<222)
- type = MT_FATSO;
- else if (r<246)
- type = MT_KNIGHT;
- else
- type = MT_BRUISER;
- newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type);
- /* killough 7/18/98: brain friendliness is transferred */
- newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
- /* killough 8/29/98: add to appropriate thread */
- P_UpdateThinker(&newmobj->thinker);
- if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */
- P_SetMobjState(newmobj, newmobj->info->seestate);
- // telefrag anything in this spot
- P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */
- // remove self (i.e., cube).
- P_RemoveMobj(mo);
- }
- void A_PlayerScream(mobj_t *mo)
- {
- int sound = sfx_pldeth; // Default death sound.
- if (gamemode != shareware && mo->health < -50)
- sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING
- S_StartSound(mo, sound);
- }
- /* cph - MBF-added codepointer functions */
- // killough 11/98: kill an object
- void A_Die(mobj_t *actor)
- {
- P_DamageMobj(actor, NULL, NULL, actor->health);
- }
- //
- // A_Detonate
- // killough 8/9/98: same as A_Explode, except that the damage is variable
- //
- void A_Detonate(mobj_t *mo)
- {
- P_RadiusAttack(mo, mo->target, mo->info->damage);
- }
- //
- // killough 9/98: a mushroom explosion effect, sorta :)
- // Original idea: Linguica
- //
- void A_Mushroom(mobj_t *actor)
- {
- int i, j, n = actor->info->damage;
- A_Explode(actor); // First make normal explosion
- // Now launch mushroom cloud
- for (i = -n; i <= n; i += 8)
- for (j = -n; j <= n; j += 8)
- {
- mobj_t target = *actor, *mo;
- target.x += i << FRACBITS; // Aim in many directions from source
- target.y += j << FRACBITS;
- target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high
- mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball
- mo->momx >>= 1;
- mo->momy >>= 1; // Slow it down a bit
- mo->momz >>= 1;
- mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity
- }
- }
- //
- // killough 11/98
- //
- // The following were inspired by Len Pitre
- //
- // A small set of highly-sought-after code pointers
- //
- void A_Spawn(mobj_t *mo)
- {
- if (mo->state->misc1)
- {
- /* mobj_t *newmobj = */
- P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z,
- mo->state->misc1 - 1);
- /* CPhipps - no friendlyness (yet)
- newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND);
- */
- }
- }
- void A_Turn(mobj_t *mo)
- {
- mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360);
- }
- void A_Face(mobj_t *mo)
- {
- mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360);
- }
- void A_Scratch(mobj_t *mo)
- {
- mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ?
- mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0,
- P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0;
- }
- void A_PlaySound(mobj_t *mo)
- {
- S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1);
- }
- void A_RandomJump(mobj_t *mo)
- {
- if (P_Random(pr_randomjump) < mo->state->misc2)
- P_SetMobjState(mo, mo->state->misc1);
- }
- //
- // This allows linedef effects to be activated inside deh frames.
- //
- void A_LineEffect(mobj_t *mo)
- {
- static line_t junk;
- player_t player;
- player_t *oldplayer;
- junk = *lines;
- oldplayer = mo->player;
- mo->player = &player;
- player.health = 100;
- junk.special = (short)mo->state->misc1;
- if (!junk.special)
- return;
- junk.tag = (short)mo->state->misc2;
- if (!P_UseSpecialLine(mo, &junk, 0))
- P_CrossSpecialLine(&junk, 0, mo);
- mo->state->misc1 = junk.special;
- mo->player = oldplayer;
- }
|