Files

1 line
32 KiB
C++

// P_enemy.c
#include "doomdef.h"
#include "p_local.h"
void A_Fall (mobj_t *actor);
/*
===============================================================================
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
===============================================================================
*/
/*
=================
=
= P_NoiseAlert
=
= If a monster yells at a player, it will alert other monsters to the player
=
=================
*/
mobj_t *soundtarget;
void P_RecursiveSound (sector_t *sec, int soundblocks)
{
int i;
line_t *check;
sector_t *other;
// 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;
sec->soundtarget = soundtarget;
for (i=0 ;i<sec->linecount ; i++)
{
check = sec->lines[i];
if (! (check->flags & ML_TWOSIDED) )
continue;
P_LineOpening (check);
if (openrange <= 0)
continue; // closed door
if ( sides[ check->sidenum[0] ].sector == sec)
other = sides[ check->sidenum[1] ] .sector;
else
other = sides[ check->sidenum[0] ].sector;
if (check->flags & ML_SOUNDBLOCK)
{
if (!soundblocks)
P_RecursiveSound (other, 1);
}
else
P_RecursiveSound (other, soundblocks);
}
}
/*
=================
=
= 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 *emmiter)
{
soundtarget = target;
validcount++;
P_RecursiveSound (emmiter->subsector->sector, 0);
}
/*
================
=
= P_CheckMeleeRange
=
================
*/
boolean P_CheckMeleeRange (mobj_t *actor)
{
mobj_t *pl;
fixed_t dist;
if (!actor->target)
return false;
pl = actor->target;
dist = P_AproxDistance (pl->x-actor->x, pl->y-actor->y);
if (dist >= MELEERANGE-20*FRACUNIT+pl->info->radius)
return false;
if (! P_CheckSight (actor, actor->target) )
return false;
return true;
}
/*
================
=
= P_CheckMissileRange
=
================
*/
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;
return true;
}
if (actor->reactiontime)
return false; // don't 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 >>= 16;
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 () < dist)
return false;
return true;
}
/*
================
=
= P_Move
=
= Move in the current direction
= returns false if the move is blocked
================
*/
fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000};
fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000};
#define MAXSPECIALCROSS 8
extern line_t *spechit[MAXSPECIALCROSS];
extern int numspechit;
boolean P_Move (mobj_t *actor)
{
fixed_t tryx, tryy;
line_t *ld;
boolean try, good;
if (actor->movedir == DI_NODIR)
return false;
if ((unsigned)actor->movedir >= 8)
I_Error ("Weird actor->movedir!");
tryx = actor->x + actor->info->speed*xspeed[actor->movedir];
tryy = actor->y + actor->info->speed*yspeed[actor->movedir];
try = P_TryMove (actor, tryx, tryy);
if (!try)
{ // open any specials
if (actor->flags & MF_FLOAT && floatok)
{ // must adjust height
if (actor->z < tmfloorz)
actor->z += FLOATSPEED;
else
actor->z -= FLOATSPEED;
actor->flags |= MF_INFLOAT;
return true;
}
if (!numspechit)
return false;
actor->movedir = DI_NODIR;
good = false;
while (numspechit--)
{
ld = spechit[numspechit];
// if the special isn't a door that can be opened, return false
if (P_UseSpecialLine (actor, ld,0))
good = true;
}
return good;
}
else
{
actor->flags &= ~MF_INFLOAT;
}
if (! (actor->flags & MF_FLOAT) )
actor->z = actor->floorz;
return true;
}
/*
==================================
=
= TryWalk
=
= Attempts to move actoron 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.
=
==================================
*/
boolean P_TryWalk (mobj_t *actor)
{
if (!P_Move (actor))
{
return false;
}
actor->movecount = P_Random()&15;
return true;
}
/*
================
=
= P_NewChaseDir
=
================
*/
dirtype_t opposite[] =
{DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
DI_NORTH, DI_NORTHWEST, DI_NODIR};
dirtype_t diags[] = {DI_NORTHWEST,DI_NORTHEAST,DI_SOUTHWEST,DI_SOUTHEAST};
void P_NewChaseDir (mobj_t *actor)
{
fixed_t deltax,deltay;
dirtype_t d[3];
dirtype_t tdir, olddir, turnaround;
if (!actor->target)
I_Error ("P_NewChaseDir: called with no target");
olddir = actor->movedir;
turnaround=opposite[olddir];
deltax = actor->target->x - actor->x;
deltay = actor->target->y - actor->y;
if (deltax>10*FRACUNIT)
d[1]= DI_EAST;
else if (deltax<-10*FRACUNIT)
d[1]= DI_WEST;
else
d[1]=DI_NODIR;
if (deltay<-10*FRACUNIT)
d[2]= DI_SOUTH;
else if (deltay>10*FRACUNIT)
d[2]= DI_NORTH;
else
d[2]=DI_NODIR;
// try direct route
if (d[1] != DI_NODIR && d[2] != DI_NODIR)
{
actor->movedir = diags[((deltay<0)<<1)+(deltax>0)];
if (actor->movedir != turnaround && P_TryWalk(actor))
return;
}
// try other directions
if (P_Random() > 200 || abs(deltay)>abs(deltax))
{
tdir=d[1];
d[1]=d[2];
d[2]=tdir;
}
if (d[1]==turnaround)
d[1]=DI_NODIR;
if (d[2]==turnaround)
d[2]=DI_NODIR;
if (d[1]!=DI_NODIR)
{
actor->movedir = d[1];
if (P_TryWalk(actor))
return; /*either moved forward or attacked*/
}
if (d[2]!=DI_NODIR)
{
actor->movedir =d[2];
if (P_TryWalk(actor))
return;
}
/* there is no direct path to the player, so pick another direction */
if (olddir!=DI_NODIR)
{
actor->movedir =olddir;
if (P_TryWalk(actor))
return;
}
if (P_Random()&1) /*randomly determine direction of search*/
{
for (tdir=DI_EAST ; tdir<=DI_SOUTHEAST ; tdir++)
{
if (tdir!=turnaround)
{
actor->movedir =tdir;
if ( P_TryWalk(actor) )
return;
}
}
}
else
{
for (tdir=DI_SOUTHEAST ; tdir != (DI_EAST-1) ;tdir--)
{
if (tdir!=turnaround)
{
actor->movedir =tdir;
if ( P_TryWalk(actor) )
return;
}
}
}
if (turnaround != DI_NODIR)
{
actor->movedir =turnaround;
if ( P_TryWalk(actor) )
return;
}
actor->movedir = DI_NODIR; // can't move
}
/*
================
=
= P_LookForPlayers
=
= If allaround is false, only look 180 degrees in front
= returns true if a player is targeted
================
*/
boolean P_LookForPlayers (mobj_t *actor, boolean allaround)
{
int c, stop;
player_t *player;
sector_t *sector;
angle_t an;
fixed_t dist;
sector = actor->subsector->sector;
c = 0;
stop = (actor->lastlook-1)&3;
for ( ; ; actor->lastlook = (actor->lastlook+1)&3 )
{
if (!playeringame[actor->lastlook])
continue;
if (c++ == 2 || actor->lastlook == stop)
return false; // done looking
player = &players[actor->lastlook];
if (player->health <= 0)
continue; // dead
if (!P_CheckSight (actor, player->mo))
continue; // out of sight
if (!allaround)
{
an = R_PointToAngle2 (actor->x, actor->y,
player->mo->x, player->mo->y) - actor->angle;
if (an > ANG90 && an < ANG270)
{
dist = P_AproxDistance (player->mo->x - actor->x,
player->mo->y - actor->y);
// if real close, react anyway
if (dist > MELEERANGE)
continue; // behind back
}
}
actor->target = player->mo;
return true;
}
return false;
}
//=============================================================================
/*
==============
=
= A_KeenDie
=
==============
*/
void A_KeenDie (mobj_t *mo)
{
thinker_t *th;
mobj_t *mo2;
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)
continue;
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->threshold = 0; // any shot will wake up
targ = actor->subsector->sector->soundtarget;
if (targ && (targ->flags & MF_SHOOTABLE) )
{
actor->target = targ;
if ( actor->flags & MF_AMBUSH )
{
if (P_CheckSight (actor, actor->target))
goto seeyou;
}
else
goto seeyou;
}
if (!P_LookForPlayers (actor, false) )
return;
// go into chase state
seeyou:
if (actor->info->seesound)
{
int sound;
switch (actor->info->seesound)
{
case sfx_posit1:
case sfx_posit2:
case sfx_posit3:
sound = sfx_posit1+P_Random()%3;
break;
case sfx_bgsit1:
case sfx_bgsit2:
sound = sfx_bgsit1+P_Random()%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_Chase
=
= Actor has a melee attack, so it tries to close as fast as possible
=
==============
*/
void A_Chase (mobj_t *actor)
{
int delta;
if (actor->reactiontime)
actor->reactiontime--;
//
// modify target threshold
//
if (actor->threshold)
{
if (!actor->target || actor->target->health <= 0)
actor->threshold = 0;
else
actor->threshold--;
}
//
// turn towards movement direction if not there yet
//
if (actor->movedir < 8)
{
actor->angle &= (7<<29);
delta = actor->angle - (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))
{ // look for a new target
if (P_LookForPlayers(actor,true))
return; // got a new target
P_SetMobjState (actor, actor->info->spawnstate);
return;
}
//
// don't 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);
return;
}
//
// check for missile attack
//
if (actor->info->missilestate)
{
if (gameskill < sk_nightmare && !fastparm && actor->movecount)
goto nomissile;
if (!P_CheckMissileRange (actor))
goto nomissile;
P_SetMobjState (actor, actor->info->missilestate);
actor->flags |= MF_JUSTATTACKED;
return;
}
nomissile:
//
// possibly choose another target
//
if (netgame && !actor->threshold && !P_CheckSight (actor, actor->target) )
{
if (P_LookForPlayers(actor,true))
return; // got a new target
}
//
// chase towards player
//
if (--actor->movecount<0 || !P_Move (actor))
{
P_NewChaseDir (actor);
}
//
// make active sound
//
if (actor->info->activesound && P_Random () < 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)
actor->angle += (P_Random()-P_Random())<<21;
}
/*
==============
=
= A_PosAttack
=
==============
*/
void A_PosAttack (mobj_t *actor)
{
int angle, damage, slope;
if (!actor->target)
return;
A_FaceTarget (actor);
angle = actor->angle;
slope = P_AimLineAttack (actor, angle, MISSILERANGE);
S_StartSound (actor, sfx_pistol);
angle += (P_Random()-P_Random())<<20;
damage = ((P_Random()%5)+1)*3;
P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
}
void A_SPosAttack (mobj_t *actor)
{
int i;
int angle, bangle, damage, slope;
if (!actor->target)
return;
S_StartSound (actor, sfx_shotgn);
A_FaceTarget (actor);
bangle = actor->angle;
slope = P_AimLineAttack (actor, bangle, MISSILERANGE);
for (i=0 ; i<3 ; i++)
{
angle = bangle + ((P_Random()-P_Random())<<20);
damage = ((P_Random()%5)+1)*3;
P_LineAttack (actor, angle, MISSILERANGE, slope, damage);
}
}
void A_CPosAttack (mobj_t *actor)
{
int angle, bangle, damage, slope;
if (!actor->target)
return;
S_StartSound (actor, sfx_shotgn);
A_FaceTarget (actor);
bangle = actor->angle;
slope = P_AimLineAttack (actor, bangle, MISSILERANGE);
angle = bangle + ((P_Random()-P_Random())<<20);
damage = ((P_Random()%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);
if (P_Random () < 40)
return;
if (!actor->target || actor->target->health <= 0 || !P_CheckSight (actor, actor->target) )
P_SetMobjState (actor, actor->info->seestate);
}
void A_SpidRefire (mobj_t *actor)
{
// keep firing unless target got out of sight
A_FaceTarget (actor);
if (P_Random () < 10)
return;
if (!actor->target || actor->target->health <= 0 || !P_CheckSight (actor, actor->target) )
P_SetMobjState (actor, actor->info->seestate);
}
void A_BspiAttack (mobj_t *actor)
{
if (!actor->target)
return;
A_FaceTarget (actor);
//
// launch a missile
//
P_SpawnMissile (actor, actor->target, MT_ARACHPLAZ);
}
/*
==============
=
= A_TroopAttack
=
==============
*/
void A_TroopAttack (mobj_t *actor)
{
int damage;
if (!actor->target)
return;
A_FaceTarget (actor);
if (P_CheckMeleeRange (actor))
{
S_StartSound (actor, sfx_claw);
damage = (P_Random()%8+1)*3;
P_DamageMobj (actor->target, actor, actor, damage);
return;
}
//
// launch a missile
//
P_SpawnMissile (actor, actor->target, MT_TROOPSHOT);
}
void A_SargAttack (mobj_t *actor)
{
int damage;
if (!actor->target)
return;
A_FaceTarget (actor);
if (P_CheckMeleeRange (actor))
{
damage = ((P_Random()%10)+1)*4;
P_DamageMobj (actor->target, actor, actor, damage);
}
}
void A_HeadAttack (mobj_t *actor)
{
int damage;
if (!actor->target)
return;
A_FaceTarget (actor);
if (P_CheckMeleeRange (actor))
{
damage = (P_Random()%6+1)*10;
P_DamageMobj (actor->target, actor, actor, damage);
return;
}
//
// launch a missile
//
P_SpawnMissile (actor, actor->target, MT_HEADSHOT);
}
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)
{
int damage;
if (!actor->target)
return;
if (P_CheckMeleeRange (actor))
{
S_StartSound (actor, sfx_claw);
damage = (P_Random()%8+1)*10;
P_DamageMobj (actor->target, actor, actor, damage);
return;
}
//
// launch a missile
//
P_SpawnMissile (actor, actor->target, MT_BRUISERSHOT);
}
//===========================================================================
/*
================
=
= 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;
mo->tracer = actor->target;
}
int TRACEANGLE = 0xc000000;
void A_Tracer (mobj_t *actor)
{
angle_t exact;
fixed_t dist, slope;
mobj_t *dest;
mobj_t *th;
if (gametic & 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()&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)
{
int damage;
if (!actor->target)
return;
A_FaceTarget (actor);
if (P_CheckMeleeRange (actor))
{
damage = ((P_Random()%10)+1)*6;
S_StartSound (actor, sfx_skepch);
P_DamageMobj (actor->target, actor, actor, damage);
}
}
//===========================================================================
/*
==================
=
= PIT_VileCheck
=
==================
*/
mobj_t *corpsehit;
mobj_t *vileobj;
fixed_t viletryx, viletryy;
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 ( abs(thing->x - viletryx) > maxdist
|| abs(thing->y - viletryy) > maxdist )
return true; // not actually touching
corpsehit = thing;
corpsehit->momx = corpsehit->momy = 0;
corpsehit->height <<= 2;
check = P_CheckPosition (corpsehit, corpsehit->x, corpsehit->y);
corpsehit->height >>= 2;
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, yl, yh;
int bx,by;
mobjinfo_t *info;
mobj_t *temp;
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++)
if (!P_BlockThingsIterator(bx,by,PIT_VileCheck))
{ // got one!
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);
corpsehit->height <<= 2;
corpsehit->flags = info->flags;
corpsehit->health = info->spawnhealth;
corpsehit->target = NULL;
return;
}
}
A_Chase (actor);
}
/*
================
=
= 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_Fire (mobj_t *actor);
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)
{
mobj_t *dest;
unsigned an;
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);
fog = P_SpawnMobj (actor->target->x, actor->target->x, actor->target->z, MT_FIRE);
actor->tracer = fog;
fog->target = actor;
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 );
}
//===========================================================================
#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;
A_FaceTarget (actor);
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;
A_FaceTarget (actor);
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;
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
=
==================
*/
void A_PainShootSkull (mobj_t *actor, angle_t angle)
{
fixed_t x,y,z;
mobj_t *newmobj;
angle_t an;
int prestep;
int count;
thinker_t *currentthinker;
//
// if there are allready 20 skulls on the level, don't spit another one
//
count = 0;
currentthinker = thinkercap.next;
while (currentthinker != &thinkercap)
{
if (currentthinker->function == P_MobjThinker
&& ((mobj_t *)currentthinker)->type == MT_SKULL)
count++;
currentthinker = currentthinker->next;
}
if (count > 20)
return;
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;
newmobj = P_SpawnMobj (x , y, z, MT_SKULL);
if (!P_TryMove (newmobj, newmobj->x, newmobj->y))
{
P_DamageMobj (newmobj,actor,actor,10000); // kill it immediately
return;
}
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 ()%3;
break;
case sfx_bgdth1:
case sfx_bgdth2:
sound = sfx_bgdth1 + P_Random ()%2;
break;
default:
sound = actor->info->deathsound;
break;
}
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;
mobj_t *mo2;
line_t junk;
int i;
if (commercial)
{
if (gamemap != 7)
return;
if ((mo->type != MT_FATSO) &&
(mo->type != MT_BABY))
return;
}
else
{
if (gamemap != 8)
return; // bruisers apear on other levels
if (mo->type == MT_BRUISER && gameepisode != 1)
return; // bruisers aren't bosses on other episodes
}
//
// 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 don't 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)
continue;
mo2 = (mobj_t *)th;
if (mo2 != mo && mo2->type == mo->type && mo2->health > 0)
return; // other boss not dead
}
//
// victory!
//
if (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
{
if (gameepisode == 1)
{
junk.tag = 666;
EV_DoFloor (&junk, lowerFloorToLowest);
return;
}
}
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_ReFire (player_t *player, pspdef_t *psp);
void A_CloseShotgun2 (player_t *player, pspdef_t *psp)
{
S_StartSound (player->mo, sfx_dbcls);
A_ReFire(player,psp);
}
//=============================================================================
mobj_t *braintargets[32];
int numbraintargets;
int braintargeton;
void A_BrainAwake (mobj_t *mo)
{
thinker_t *thinker;
mobj_t *m;
// find all the target spots
numbraintargets = 0;
braintargeton = 0;
thinker = thinkercap.next;
for (thinker = thinkercap.next ; thinker != &thinkercap
; thinker = thinker->next)
{
if (thinker->function != P_MobjThinker)
continue; // not a mobj
m = (mobj_t *)thinker;
if (m->type == MT_BOSSTARGET )
{
braintargets[numbraintargets] = m;
numbraintargets++;
}
}
S_StartSound (NULL,sfx_bossit);
}
void A_BrainPain (mobj_t *mo)
{
S_StartSound (NULL,sfx_bospn);
}
void A_BrainScream (mobj_t *mo)
{
int x,y,z;
mobj_t *th;
for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8)
{
y = mo->y - 320*FRACUNIT;
z = 128 + P_Random()*2*FRACUNIT;
th = P_SpawnMobj (x,y,z, MT_ROCKET);
th->momz = P_Random()*512;
P_SetMobjState (th, S_BRAINEXPLODE1);
th->tics -= P_Random()&7;
if (th->tics < 1)
th->tics = 1;
}
S_StartSound (NULL,sfx_bosdth);
}
void A_BrainExplode (mobj_t *mo)
{
int x,y,z;
mobj_t *th;
x = mo->x + (P_Random () - P_Random ())*2048;
y = mo->y;
z = 128 + P_Random()*2*FRACUNIT;
th = P_SpawnMobj (x,y,z, MT_ROCKET);
th->momz = P_Random()*512;
P_SetMobjState (th, S_BRAINEXPLODE1);
th->tics -= P_Random()&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;
mobj_t *newmobj;
static int easy = 0;
easy ^= 1;
if (gameskill <= sk_easy && (!easy))
return;
// shoot a cube at current target
targ = braintargets[braintargeton];
braintargeton = (braintargeton+1)%numbraintargets;
// spawn brain missile
newmobj = P_SpawnMissile (mo, targ, MT_SPAWNSHOT);
newmobj->target = targ;
newmobj->reactiontime = ((targ->y - mo->y)/newmobj->momy) / newmobj->state->tics;
S_StartSound(NULL, sfx_bospit);
}
void A_SpawnFly (mobj_t *mo);
void A_SpawnSound (mobj_t *mo) // travelling cube sound
{
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;
// teleport fog
fog = P_SpawnMobj (targ->x, targ->y, targ->z, MT_SPAWNFIRE);
S_StartSound (fog, sfx_telept);
// monster
r = P_Random ();
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);
if (P_LookForPlayers (newmobj, true) )
P_SetMobjState (newmobj, newmobj->info->seestate);
// telefrag anything in this spot
P_TeleportMove (newmobj, newmobj->x, newmobj->y);
// remove self
P_RemoveMobj (mo);
}
void A_PlayerScream (mobj_t *mo)
{
int sound = sfx_pldeth;
if (commercial &&
// IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING
mo->health < -50)
sound = sfx_pdiehi;
S_StartSound (mo, sound);
}