#include #include #include "aizombf.h" #include "actor.h" #include "db.h" #include "debug4g.h" #include "dude.h" #include "engine.h" #include "eventq.h" #include "gameutil.h" #include "globals.h" #include "misc.h" #include "multi.h" #include "player.h" #include "trig.h" /**************************************************************************** ** LOCAL CONSTANTS ****************************************************************************/ #define kFatZombieMeleeDist M2X(2.0) //M2X(1.6) #define kFatZombiePukeDist1 M2X(3) #define kFatZombiePukeDist2 M2X(5) #define kFatZombieThrowDist1 M2X(10) #define kFatZombieThrowDist2 M2X(6) /**************************************************************************** ** LOCAL callback prototypes ****************************************************************************/ static void HackCallback( int /* type */, int nXIndex ); static void PukeCallback( int /* type */, int nXIndex ); static void ThrowCallback( int /* type */, int nXIndex ); /**************************************************************************** ** LOCAL think/move function prototypes ****************************************************************************/ static void thinkSearch( SPRITE *pSprite, XSPRITE *pXSprite ); static void thinkGoto( SPRITE *pSprite, XSPRITE *pXSprite ); static void thinkChase( SPRITE *pSprite, XSPRITE *pXSprite ); /**************************************************************************** ** GLOBAL CONSTANTS ****************************************************************************/ AISTATE zombieFIdle = { kSeqDudeIdle, NULL, 0, NULL, NULL, aiThinkTarget, NULL }; AISTATE zombieFChase = { kSeqFatZombieWalk, NULL, 0, NULL, aiMoveForward, thinkChase, NULL }; AISTATE zombieFGoto = { kSeqFatZombieWalk, NULL, 3600, NULL, aiMoveForward, thinkGoto, &zombieFIdle }; AISTATE zombieFHack = { kSeqFatZombieAttack,HackCallback, 120, NULL, NULL, NULL, &zombieFChase }; AISTATE zombieFPuke = { kSeqFatZombieAttack,PukeCallback, 120, NULL, NULL, NULL, &zombieFChase }; AISTATE zombieFThrow = { kSeqFatZombieAttack,ThrowCallback, 120, NULL, NULL, NULL, &zombieFChase }; AISTATE zombieFSearch = { kSeqFatZombieWalk, NULL, 3600, NULL, aiMoveForward, thinkSearch, &zombieFIdle }; AISTATE zombieFRecoil = { kSeqDudeRecoil, NULL, 0, NULL, NULL, NULL, &zombieFChase }; /**************************************************************************** ** LOCAL FUNCTIONS ****************************************************************************/ /**************************************************************************** ** HackCallback() ** ** ****************************************************************************/ static void HackCallback( int /* type */, int nXIndex ) { XSPRITE *pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; SPRITE *pSprite = &sprite[nSprite]; int dx = Cos(pSprite->ang) >> 16; int dy = Sin(pSprite->ang) >> 16; int dz = 0; actFireVector(nSprite, pSprite->z, dx, dy, dz, kVectorCleaver); } /**************************************************************************** ** HackCallback() ** ** ****************************************************************************/ static void PukeCallback( int /* type */, int nXIndex ) { XSPRITE *pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; SPRITE *pSprite = &sprite[nSprite]; int dx = Cos(pSprite->ang) >> 16; int dy = Sin(pSprite->ang) >> 16; int dz = 0; int nThing = actFireThing(nSprite, pSprite->z, 0, kThingTNTStick, (M2X(4.0) << 4) / kTimerRate); evPost(nThing, SS_SPRITE, 2 * kTimerRate); // 2 second burn off } /**************************************************************************** ** ThrowCallback() ** ** ****************************************************************************/ static void ThrowCallback( int /* type */, int nXIndex ) { XSPRITE *pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; SPRITE *pSprite = &sprite[nSprite]; int dx = Cos(pSprite->ang) >> 16; int dy = Sin(pSprite->ang) >> 16; int dz = 0; int nZOffset = dudeInfo[pSprite->type - kDudeBase].eyeHeight; actFireMissile( nSprite, pSprite->z - nZOffset, dx, dy, dz, kMissileButcherKnife ); } /**************************************************************************** ** thinkSearch() ** ** ****************************************************************************/ static void thinkSearch( SPRITE *pSprite, XSPRITE *pXSprite ) { aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); aiThinkTarget(pSprite, pXSprite); } /**************************************************************************** ** thinkGoto() ** ** ****************************************************************************/ static void thinkGoto( SPRITE *pSprite, XSPRITE *pXSprite ) { int dx, dy, dist; dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; SPRITE *pTarget = &sprite[pXSprite->target]; XSPRITE *pXTarget = &xsprite[pTarget->extra]; dx = pXSprite->targetX - pSprite->x; dy = pXSprite->targetY - pSprite->y; int nAngle = getangle(dx, dy); dist = qdist(dx, dy); aiChooseDirection(pSprite, pXSprite, nAngle); // if reached target, change to search mode if ( dist < M2X(1.0) && qabs(pSprite->ang - nAngle) < pDudeInfo->periphery ) aiNewState(pSprite, pXSprite, &zombieFSearch); aiThinkTarget(pSprite, pXSprite); } /**************************************************************************** ** thinkChase() ** ** ****************************************************************************/ static void thinkChase( SPRITE *pSprite, XSPRITE *pXSprite ) { if ( pXSprite->target == -1) { aiNewState(pSprite, pXSprite, &zombieFGoto); return; } int dx, dy, dist; dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type - kDudeBase]; dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); SPRITE *pTarget = &sprite[pXSprite->target]; XSPRITE *pXTarget = &xsprite[pTarget->extra]; // check target dx = pTarget->x - pSprite->x; dy = pTarget->y - pSprite->y; aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); if ( pXTarget->health == 0 ) { // target is dead aiNewState(pSprite, pXSprite, &zombieFSearch); return; } if ( IsPlayerSprite( pTarget ) ) { PLAYER *pPlayer = &gPlayer[ pTarget->type - kDudePlayer1 ]; if ( powerupCheck( pPlayer, kItemLtdInvisibility - kItemBase ) > 0 || powerupCheck( pPlayer, kItemDeathMask - kItemBase ) > 0 ) { aiNewState(pSprite, pXSprite, &zombieFSearch); return; } } dist = qdist(dx, dy); if ( dist <= pDudeInfo->seeDist ) { int nAngle = getangle(dx, dy); int losAngle = ((kAngle180 + nAngle - pSprite->ang) & kAngleMask) - kAngle180; int eyeAboveZ = pDudeInfo->eyeHeight * pSprite->yrepeat << 2; // is there a line of sight to the target? if ( cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - eyeAboveZ, pSprite->sectnum) ) { // is the target visible? if ( dist < pDudeInfo->seeDist && qabs(losAngle) <= pDudeInfo->periphery ) { aiSetTarget(pXSprite, pXSprite->target); // check to see if we can attack if ( dist < kFatZombieThrowDist1 && dist > kFatZombieThrowDist2 && qabs(losAngle) < kAngle15 ) aiNewState(pSprite, pXSprite, &zombieFThrow); else if ( dist < kFatZombiePukeDist1 && dist > kFatZombiePukeDist2 && qabs(losAngle) < kAngle15 ) aiNewState(pSprite, pXSprite, &zombieFPuke); else if ( dist < kFatZombieMeleeDist && qabs(losAngle) < kAngle15 ) aiNewState(pSprite, pXSprite, &zombieFHack); return; } } } dprintf("Fat zombie %d lost sight of target %d\n", pXSprite->reference, pXSprite->target); aiNewState(pSprite, pXSprite, &zombieFSearch); pXSprite->target = -1; }