Iskhodnyy_kod_Doom
.pdf#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
//
381
//
//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
382
( 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) |
|
383
{ |
|
|
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!");
384
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;
}
385
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;
386
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) )
387
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 |
|
388
}
}
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;
389
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
void A_Chase (mobj_t* |
actor) |
{ |
|
int |
delta; |
if (actor->reactiontime) actor->reactiontime--;
390