?? p_enemy.c
字號:
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Enemy thinking, AI.
// Action Pointer Functions
// that are associated with states/frames.
//
//-----------------------------------------------------------------------------
static const char
rcsid[] = "$Id: p_enemy.c,v 1.5 1997/02/03 22:45:11 b1 Exp $";
#include <stdlib.h>
#include "m_random.h"
#include "i_system.h"
#include "doomdef.h"
#include "p_local.h"
#include "s_sound.h"
#include "g_game.h"
// State.
#include "doomstat.h"
#include "r_state.h"
// Data.
#include "sounds.h"
typedef enum
{
DI_EAST,
DI_NORTHEAST,
DI_NORTH,
DI_NORTHWEST,
DI_WEST,
DI_SOUTHWEST,
DI_SOUTH,
DI_SOUTHEAST,
DI_NODIR,
NUMDIRS
} dirtype_t;
//
// P_NewChaseDir related LUT.
//
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 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
//
//
// Called by P_NoiseAlert.
// Recursively traverse adjacent sectors,
// sound blocking lines cut off traversal.
//
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; // 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 >>= 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;
fixed_t tryy;
line_t* ld;
// warning: 'catch', 'throw', and 'try'
// are all C++ reserved words
boolean try_ok;
boolean 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_ok = P_TryMove (actor, tryx, tryy);
if (!try_ok)
{
// 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 is not 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 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.
//
boolean P_TryWalk (mobj_t* actor)
{
if (!P_Move (actor))
{
return false;
}
actor->movecount = P_Random()&15;
return true;
}
void P_NewChaseDir (mobj_t* actor)
{
fixed_t deltax;
fixed_t deltay;
dirtype_t d[3];
int tdir;
dirtype_t olddir;
dirtype_t 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))
{
// either moved forward or attacked
return;
}
}
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;
}
// randomly determine direction of search
if (P_Random()&1)
{
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 not 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;
int 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)
{
// done looking
return false;
}
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
// DOOM II special, map 32.
// Uses special tag 666.
//
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.acp1 != (actionf_p1)P_MobjThinker)
continue;
mo2 = (mobj_t *)th;
if (mo2 != mo
&& mo2->type == mo->type
&& mo2->health > 0)
{
// other Keen not dead
return;
}
}
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)
{
// full volume
S_StartSound (NULL, sound);
}
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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -