#include #include #include "aigarg.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 "sfx.h" #include "trig.h" /**************************************************************************** ** LOCAL CONSTANTS ****************************************************************************/ #define kFGargoyleMeleeDist M2X(2.0) #define kFGargoyleThrowDist1 M2X(12) // thrown bone #define kFGargoyleThrowDist2 M2X(6) #define kSGargoyleMeleeDist M2X(2.0) #define kSGargoyleBlastDist1 M2X(20) // thrown paralyzing blast #define kSGargoyleBlastDist2 M2X(14) #define kSlopeThrow -7168 /**************************************************************************** ** LOCAL callback prototypes ****************************************************************************/ // flesh gargoyle callbacks static void SlashFCallback( int /* type */, int nXIndex ); static void ThrowFCallback( int /* type */, int nXIndex ); // stone gargoyle callbacks //static void SlashSCallback( int /* type */, int nXIndex ); //static void ThrowSCallback( int /* type */, int nXIndex ); /**************************************************************************** ** LOCAL think/move/entry function prototypes ****************************************************************************/ static void thinkSearch( SPRITE *pSprite, XSPRITE *pXSprite ); static void thinkGoto( SPRITE *pSprite, XSPRITE *pXSprite ); static void thinkChase( SPRITE *pSprite, XSPRITE *pXSprite ); static void entryFStatue( SPRITE *pSprite, XSPRITE */*pXSprite*/ ); /**************************************************************************** ** GLOBAL CONSTANTS ****************************************************************************/ AISTATE gargoyleFIdle = { kSeqDudeIdle, NULL, 0, NULL, NULL, aiThinkTarget, NULL }; AISTATE gargoyleFChase = { kSeqDudeIdle, NULL, 0, NULL, aiMoveForward, thinkChase, NULL }; AISTATE gargoyleFGoto = { kSeqDudeIdle, NULL, 3600, NULL, aiMoveForward, thinkGoto, &gargoyleFIdle }; AISTATE gargoyleFSlash = { kSeqGargoyleAttack, SlashFCallback, 120, NULL, NULL, NULL, &gargoyleFChase }; AISTATE gargoyleFThrow = { kSeqGargoyleAttack, ThrowFCallback, 120, NULL, NULL, NULL, &gargoyleFChase }; AISTATE gargoyleFRecoil = { kSeqDudeRecoil, NULL, 0, NULL, NULL, NULL, &gargoyleFChase }; AISTATE gargoyleFSearch = { kSeqDudeIdle, NULL, 3600, NULL, aiMoveForward, thinkSearch, &gargoyleFIdle }; AISTATE gargoyleFMorph2 = { -1, NULL, 0, entryFStatue, NULL, NULL, &gargoyleFSearch }; AISTATE gargoyleFMorph = { kSeqFleshStatueTurn,NULL, 0, NULL, NULL, NULL, &gargoyleFMorph2 }; //AISTATE fleshGargoyleDodge = { kSeqDudeIdle, NULL, 90, NULL, aiMoveDodge, NULL, &gargoyleFChase }; //AISTATE fleshGargoyleSwoop = { kSeqGargoyleFly, NULL, 0, NULL, aiMoveForward, thinkChase, NULL }; /**************************************************************************** ** LOCAL FUNCTIONS ****************************************************************************/ /**************************************************************************** ** SlashFCallback() ** ** ****************************************************************************/ static void SlashFCallback( 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, kVectorClaw); } /**************************************************************************** ** ThrowFCallback() ** ** ****************************************************************************/ static void ThrowFCallback( int /* type */, int nXIndex ) { XSPRITE *pXSprite = &xsprite[nXIndex]; int nSprite = pXSprite->reference; SPRITE *pSprite = &sprite[nSprite]; int nThing = actFireThing( nSprite, pSprite->z, gDudeSlope[nXIndex] + kSlopeThrow, kThingBoneClub, (M2X(14.0) << 4) / kTimerRate); } /**************************************************************************** ** 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, &gargoyleFSearch); aiThinkTarget(pSprite, pXSprite); } /**************************************************************************** ** thinkChase() ** ** ****************************************************************************/ static void thinkChase( SPRITE *pSprite, XSPRITE *pXSprite ) { if ( pXSprite->target == -1) { aiNewState(pSprite, pXSprite, &gargoyleFGoto); 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, &gargoyleFSearch); return; } if ( IsPlayerSprite( pTarget ) ) { PLAYER *pPlayer = &gPlayer[ pTarget->type - kDudePlayer1 ]; if ( powerupCheck( pPlayer, kItemLtdInvisibility - kItemBase ) > 0 ) { aiNewState(pSprite, pXSprite, &gargoyleFSearch); 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 switch ( pSprite->type ) { case kDudeFleshGargoyle: if ( dist < kFGargoyleThrowDist1 && dist > kFGargoyleThrowDist2 && qabs(losAngle) < kAngle15 ) aiNewState(pSprite, pXSprite, &gargoyleFThrow); else if ( dist < kFGargoyleMeleeDist && qabs(losAngle) < kAngle15 ) { aiNewState(pSprite, pXSprite, &gargoyleFSlash); sfxStart3DSound(pSprite->extra, kSfxGargAttack); } break; } return; } } } dprintf("Gargoyle %d lost sight of target %d\n", pXSprite->reference, pXSprite->target); aiNewState(pSprite, pXSprite, &gargoyleFGoto); pXSprite->target = -1; } static void entryFStatue( SPRITE *pSprite, XSPRITE */*pXSprite*/ ) { pSprite->type = kDudeFleshGargoyle; }