/ source / blood / src / aigarg.cpp
aigarg.cpp
  1  //-------------------------------------------------------------------------
  2  /*
  3  Copyright (C) 2010-2019 EDuke32 developers and contributors
  4  Copyright (C) 2019 Nuke.YKT
  5  
  6  This file is part of NBlood.
  7  
  8  NBlood is free software; you can redistribute it and/or
  9  modify it under the terms of the GNU General Public License version 2
 10  as published by the Free Software Foundation.
 11  
 12  This program is distributed in the hope that it will be useful,
 13  but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 15  
 16  See the GNU General Public License for more details.
 17  
 18  You should have received a copy of the GNU General Public License
 19  along with this program; if not, write to the Free Software
 20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 21  */
 22  //-------------------------------------------------------------------------
 23  #include "compat.h"
 24  #include "build.h"
 25  #include "pragmas.h"
 26  #include "mmulti.h"
 27  #include "common_game.h"
 28  
 29  
 30  #include "actor.h"
 31  #include "ai.h"
 32  #include "aigarg.h"
 33  #include "blood.h"
 34  #include "db.h"
 35  #include "dude.h"
 36  #include "eventq.h"
 37  #include "levels.h"
 38  #include "player.h"
 39  #include "seq.h"
 40  #include "sfx.h"
 41  #include "trig.h"
 42  #ifdef NOONE_EXTENSIONS
 43  #include "nnexts.h"
 44  #endif
 45  
 46  static void SlashFSeqCallback(int, int);
 47  static void ThrowFSeqCallback(int, int);
 48  static void BlastSSeqCallback(int, int);
 49  static void ThrowSSeqCallback(int, int);
 50  static void thinkTarget(spritetype *, XSPRITE *);
 51  static void thinkSearch(spritetype *, XSPRITE *);
 52  static void thinkGoto(spritetype *, XSPRITE *);
 53  static void MoveDodgeUp(spritetype *, XSPRITE *);
 54  static void MoveDodgeDown(spritetype *, XSPRITE *);
 55  static void thinkChase(spritetype *, XSPRITE *);
 56  static void entryFStatue(spritetype *, XSPRITE *);
 57  static void entrySStatue(spritetype *, XSPRITE *);
 58  static void MoveForward(spritetype *, XSPRITE *);
 59  static void MoveSlow(spritetype *, XSPRITE *);
 60  static void MoveSwoop(spritetype *, XSPRITE *);
 61  static void MoveFly(spritetype *, XSPRITE *);
 62  static void playStatueBreakSnd(spritetype*,XSPRITE*);
 63  
 64  static int nSlashFClient = seqRegisterClient(SlashFSeqCallback);
 65  static int nThrowFClient = seqRegisterClient(ThrowFSeqCallback);
 66  static int nThrowSClient = seqRegisterClient(ThrowSSeqCallback);
 67  static int nBlastSClient = seqRegisterClient(BlastSSeqCallback);
 68  
 69  AISTATE gargoyleFIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, thinkTarget, NULL };
 70  AISTATE gargoyleStatueIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, NULL, NULL };
 71  AISTATE gargoyleFChase = { kAiStateChase, 0, -1, 0, NULL, MoveForward, thinkChase, &gargoyleFIdle };
 72  AISTATE gargoyleFGoto = { kAiStateMove, 0, -1, 600, NULL, MoveForward, thinkGoto, &gargoyleFIdle };
 73  AISTATE gargoyleFSlash = { kAiStateChase, 6, nSlashFClient, 120, NULL, NULL, NULL, &gargoyleFChase };
 74  AISTATE gargoyleFThrow = { kAiStateChase, 6, nThrowFClient, 120, NULL, NULL, NULL, &gargoyleFChase };
 75  AISTATE gargoyleSThrow = { kAiStateChase, 6, nThrowSClient, 120, NULL, MoveForward, NULL, &gargoyleFChase };
 76  AISTATE gargoyleSBlast = { kAiStateChase, 7, nBlastSClient, 60, NULL, MoveSlow, NULL, &gargoyleFChase };
 77  AISTATE gargoyleFRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &gargoyleFChase };
 78  AISTATE gargoyleFSearch = { kAiStateSearch, 0, -1, 120, NULL, MoveForward, thinkSearch, &gargoyleFIdle };
 79  AISTATE gargoyleFMorph2 = { kAiStateOther, -1, -1, 0, entryFStatue, NULL, NULL, &gargoyleFIdle };
 80  AISTATE gargoyleFMorph = { kAiStateOther, 6, -1, 0, NULL, NULL, NULL, &gargoyleFMorph2 };
 81  AISTATE gargoyleSMorph2 = { kAiStateOther, -1, -1, 0, entrySStatue, NULL, NULL, &gargoyleFIdle };
 82  AISTATE gargoyleSMorph = { kAiStateOther, 6, -1, 0, NULL, NULL, NULL, &gargoyleSMorph2 };
 83  AISTATE gargoyleSwoop = { kAiStateOther, 0, -1, 120, NULL, MoveSwoop, thinkChase, &gargoyleFChase };
 84  AISTATE gargoyleFly = { kAiStateMove, 0, -1, 120, NULL, MoveFly, thinkChase, &gargoyleFChase };
 85  AISTATE gargoyleTurn = { kAiStateMove, 0, -1, 120, NULL, aiMoveTurn, NULL, &gargoyleFChase };
 86  AISTATE gargoyleDodgeUp = { kAiStateMove, 0, -1, 60, NULL, MoveDodgeUp, NULL, &gargoyleFChase };
 87  AISTATE gargoyleFDodgeUpRight = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeUp, NULL, &gargoyleFChase };
 88  AISTATE gargoyleFDodgeUpLeft = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeUp, NULL, &gargoyleFChase };
 89  AISTATE gargoyleDodgeDown = { kAiStateMove, 0, -1, 120, NULL, MoveDodgeDown, NULL, &gargoyleFChase };
 90  AISTATE gargoyleFDodgeDownRight = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeDown, NULL, &gargoyleFChase };
 91  AISTATE gargoyleFDodgeDownLeft = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeDown, NULL, &gargoyleFChase };
 92  
 93  AISTATE statueFBreakSEQ = { kAiStateOther, 5, -1, 0, entryFStatue, NULL, playStatueBreakSnd, &gargoyleFMorph2};
 94  AISTATE statueSBreakSEQ = { kAiStateOther, 5, -1, 0, entrySStatue, NULL, playStatueBreakSnd, &gargoyleSMorph2};
 95  
 96  static void playStatueBreakSnd(spritetype* pSprite, XSPRITE* pXSprite) {
 97      UNREFERENCED_PARAMETER(pXSprite);
 98      aiPlay3DSound(pSprite, 313, AI_SFX_PRIORITY_1, -1);
 99  }
100  
101  inline void SlashFSeqCallbackFixed(spritetype *pSprite, XSPRITE *pXSprite, spritetype *pTarget)
102  {
103      int tx = pXSprite->targetX-pSprite->x;
104      int ty = pXSprite->targetY-pSprite->y;
105      int nAngle = getangle(tx, ty);
106      int dx = Cos(nAngle)>>16;
107      int dy = Sin(nAngle)>>16;
108      int dz = pTarget->z-pSprite->z;
109      const int bakVecDist = gVectorData[kVectorGargSlash].maxDist;
110      if (pSprite->type == kDudeGargoyleStone) // only increase slash distance by 150% for Cheogh
111          gVectorData[kVectorGargSlash].maxDist += gVectorData[kVectorGargSlash].maxDist>>1;
112      actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash);
113      int r1 = Random(50);
114      int r2 = Random(50);
115      actFireVector(pSprite, 0, 0, dx+r2, dy-r1, dz, kVectorGargSlash);
116      r1 = Random(50);
117      r2 = Random(50);
118      actFireVector(pSprite, 0, 0, dx-r2, dy+r1, dz, kVectorGargSlash);
119      if (pSprite->type == kDudeGargoyleStone)
120          gVectorData[kVectorGargSlash].maxDist = bakVecDist;
121  }
122  
123  static void SlashFSeqCallback(int, int nXSprite)
124  {
125      XSPRITE *pXSprite = &xsprite[nXSprite];
126      int nSprite = pXSprite->reference;
127      spritetype *pSprite = &sprite[nSprite];
128      spritetype *pTarget = &sprite[pXSprite->target];
129      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
130      DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type);
131      if (EnemiesNotBlood() && !VanillaMode()) // use fixed calculation and increase vector distance
132          return SlashFSeqCallbackFixed(pSprite, pXSprite, pTarget);
133      int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2;
134      int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2;
135      int dz = height-height2;
136      int dx = Cos(pSprite->ang)>>16;
137      int dy = Sin(pSprite->ang)>>16;
138      actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash);
139      int r1 = Random(50);
140      int r2 = Random(50);
141      actFireVector(pSprite, 0, 0, dx+r2, dy-r1, dz, kVectorGargSlash);
142      r1 = Random(50);
143      r2 = Random(50);
144      actFireVector(pSprite, 0, 0, dx-r2, dy+r1, dz, kVectorGargSlash);
145  }
146  
147  static void ThrowFSeqCallback(int, int nXSprite)
148  {
149      XSPRITE *pXSprite = &xsprite[nXSprite];
150      int nSprite = pXSprite->reference;
151      spritetype *pSprite = &sprite[nSprite];
152      if (!sectRangeIsFine(pSprite->sectnum)) // invalid sector, abort
153          return;
154      actFireThing(pSprite, 0, 0, gDudeSlope[nXSprite]-7500, kThingBone, 0xeeeee);
155  }
156  
157  static void BlastSSeqCallback(int, int nXSprite)
158  {
159      XSPRITE *pXSprite = &xsprite[nXSprite];
160      int nSprite = pXSprite->reference;
161      spritetype *pSprite = &sprite[nSprite];
162      wrand(); // ???
163      spritetype *pTarget = &sprite[pXSprite->target];
164      int height = (pSprite->yrepeat*getDudeInfo(pSprite->type)->eyeHeight) << 2;
165      int dx = pXSprite->targetX-pSprite->x;
166      int dy = pXSprite->targetY-pSprite->y;
167      int UNUSED(nDist) = approxDist(dx, dy);
168      int UNUSED(nAngle) = getangle(dx, dy);
169      int x = pSprite->x;
170      int y = pSprite->y;
171      int z = height;
172      TARGETTRACK tt = { 0x10000, 0x10000, 0x100, 0x55, 0x1aaaaa };
173      Aim aim;
174      aim.dx = Cos(pSprite->ang)>>16;
175      aim.dy = Sin(pSprite->ang)>>16;
176      aim.dz = gDudeSlope[nXSprite];
177      int nClosest = 0x7fffffff;
178      for (short nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
179      {
180          spritetype *pSprite2 = &sprite[nSprite2];
181          if (pSprite == pSprite2 || !(pSprite2->flags&8))
182              continue;
183          int x2 = pSprite2->x;
184          int y2 = pSprite2->y;
185          int z2 = pSprite2->z;
186          int nDist = approxDist(x2-x, y2-y);
187          if (nDist == 0 || nDist > 0x2800)
188              continue;
189          if (tt.at10)
190          {
191              int t = divscale12(nDist, tt.at10);
192              x2 += (xvel[nSprite2]*t)>>12;
193              y2 += (yvel[nSprite2]*t)>>12;
194              z2 += (zvel[nSprite2]*t)>>8;
195          }
196          int tx = x+mulscale30(Cos(pSprite->ang), nDist);
197          int ty = y+mulscale30(Sin(pSprite->ang), nDist);
198          int tz = z+mulscale10(gDudeSlope[nXSprite], nDist);
199          int tsr = mulscale10(9460, nDist);
200          int top, bottom;
201          GetSpriteExtents(pSprite2, &top, &bottom);
202          if (tz-tsr > bottom || tz+tsr < top)
203          {
204              if (IsDudeSprite(pSprite2) && EnemiesNotBlood() && !VanillaMode()) // use fixed calculation for missile projectile
205                  aim.dz = divscale10(pSprite2->z-pSprite->z, ClipHigh(nDist, 0x1800));
206              continue;
207          }
208          int dx = (tx-x2)>>4;
209          int dy = (ty-y2)>>4;
210          int dz = (tz-z2)>>8;
211          int nDist2 = ksqrt(dx*dx+dy*dy+dz*dz);
212          if (nDist2 < nClosest)
213          {
214              int nAngle = getangle(x2-x, y2-y);
215              int nDeltaAngle = ((nAngle-pSprite->ang+1024)&2047)-1024;
216              if (klabs(nDeltaAngle) <= tt.at8)
217              {
218                  int tz = pSprite2->z-pSprite->z;
219                  if (cansee(x, y, z, pSprite->sectnum, x2, y2, z2, pSprite2->sectnum))
220                  {
221                      nClosest = nDist2;
222                      aim.dx = Cos(nAngle)>>16;
223                      aim.dy = Sin(nAngle)>>16;
224                      aim.dz = divscale10(tz, nDist);
225                      if (IsDudeSprite(pSprite2) && EnemiesNotBlood() && !VanillaMode()) // use fixed calculation for missile projectile
226                          continue;
227                      if (tz > -0x333)
228                          aim.dz = divscale10(tz, nDist);
229                      else if (tz < -0x333 && tz > -0xb33)
230                          aim.dz = divscale10(tz, nDist)+9460;
231                      else if (tz < -0xb33 && tz > -0x3000)
232                          aim.dz = divscale10(tz, nDist)+9460;
233                      else if (tz < -0x3000)
234                          aim.dz = divscale10(tz, nDist)-7500;
235                      else
236                          aim.dz = divscale10(tz, nDist);
237                  }
238                  else
239                      aim.dz = divscale10(tz, nDist);
240              }
241          }
242      }
243      #ifdef NOONE_EXTENSIONS
244          // allow to fire missile in non-player targets
245          if (IsPlayerSprite(pTarget) || gModernMap) {
246              actFireMissile(pSprite, -120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle);
247              actFireMissile(pSprite, 120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle);
248          }
249      #else
250          if (IsPlayerSprite(pTarget)) {
251              actFireMissile(pSprite, -120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle);
252              actFireMissile(pSprite, 120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle);
253          }
254      #endif
255  
256  }
257  
258  static void ThrowSSeqCallback(int, int nXSprite)
259  {
260      XSPRITE *pXSprite = &xsprite[nXSprite];
261      int nSprite = pXSprite->reference;
262      spritetype *pSprite = &sprite[nSprite];
263      if (!sectRangeIsFine(pSprite->sectnum)) // invalid sector, abort
264          return;
265      actFireThing(pSprite, 0, 0, gDudeSlope[nXSprite]-7500, kThingBone, Chance(0x6000) ? 0x133333 : 0x111111);
266  }
267  
268  static void thinkTarget(spritetype *pSprite, XSPRITE *pXSprite)
269  {
270      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
271      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
272          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
273          return;
274      }
275      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
276      DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats;
277      if (pDudeExtraE->active && pDudeExtraE->thinkTime < 10)
278          pDudeExtraE->thinkTime++;
279      else if (pDudeExtraE->thinkTime >= 10 && pDudeExtraE->active)
280      {
281          pXSprite->goalAng += 256;
282          POINT3D *pTarget = &baseSprite[pSprite->index];
283          aiSetTarget(pXSprite, pTarget->x, pTarget->y, pTarget->z);
284          aiNewState(pSprite, pXSprite, &gargoyleTurn);
285          return;
286      }
287      if (Chance(pDudeInfo->alertChance))
288      {
289          for (int p = connecthead; p >= 0; p = connectpoint2[p])
290          {
291              PLAYER *pPlayer = &gPlayer[p];
292              if (pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0)
293                  continue;
294              int x = pPlayer->pSprite->x;
295              int y = pPlayer->pSprite->y;
296              int z = pPlayer->pSprite->z;
297              int nSector = pPlayer->pSprite->sectnum;
298              int dx = x-pSprite->x;
299              int dy = y-pSprite->y;
300              int nDist = approxDist(dx, dy);
301              if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist)
302                  continue;
303              if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum))
304                  continue;
305              int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
306              if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
307              {
308                  pDudeExtraE->thinkTime = 0;
309                  aiSetTarget(pXSprite, pPlayer->nSprite);
310                  aiActivateDude(pSprite, pXSprite);
311              }
312              else if (nDist < pDudeInfo->hearDist)
313              {
314                  pDudeExtraE->thinkTime = 0;
315                  aiSetTarget(pXSprite, x, y, z);
316                  aiActivateDude(pSprite, pXSprite);
317              }
318              else
319                  continue;
320              break;
321          }
322      }
323  }
324  
325  static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite)
326  {
327      aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
328      aiLookForTarget(pSprite, pXSprite);
329  }
330  
331  static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite)
332  {
333      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
334      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
335          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
336          return;
337      }
338      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
339      int dx = pXSprite->targetX-pSprite->x;
340      int dy = pXSprite->targetY-pSprite->y;
341      int nAngle = getangle(dx, dy);
342      int nDist = approxDist(dx, dy);
343      aiChooseDirection(pSprite, pXSprite, nAngle);
344      if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
345          aiNewState(pSprite, pXSprite, &gargoyleFSearch);
346      aiThinkTarget(pSprite, pXSprite);
347  }
348  
349  static void MoveDodgeUp(spritetype *pSprite, XSPRITE *pXSprite)
350  {
351      int nSprite = pSprite->index;
352      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
353      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
354          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
355          return;
356      }
357      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
358      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
359      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
360      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
361      int nCos = Cos(pSprite->ang);
362      int nSin = Sin(pSprite->ang);
363      int dx = xvel[nSprite];
364      int dy = yvel[nSprite];
365      int t1 = dmulscale30(dx, nCos, dy, nSin);
366      int t2 = dmulscale30(dx, nSin, -dy, nCos);
367      if (pXSprite->dodgeDir > 0)
368          t2 += pDudeInfo->sideSpeed;
369      else
370          t2 -= pDudeInfo->sideSpeed;
371  
372      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
373      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
374      zvel[nSprite] = -0x1d555;
375  }
376  
377  static void MoveDodgeDown(spritetype *pSprite, XSPRITE *pXSprite)
378  {
379      int nSprite = pSprite->index;
380      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
381      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
382          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
383          return;
384      }
385      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
386      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
387      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
388      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
389      if (pXSprite->dodgeDir == 0)
390          return;
391      int nCos = Cos(pSprite->ang);
392      int nSin = Sin(pSprite->ang);
393      int dx = xvel[nSprite];
394      int dy = yvel[nSprite];
395      int t1 = dmulscale30(dx, nCos, dy, nSin);
396      int t2 = dmulscale30(dx, nSin, -dy, nCos);
397      if (pXSprite->dodgeDir > 0)
398          t2 += pDudeInfo->sideSpeed;
399      else
400          t2 -= pDudeInfo->sideSpeed;
401  
402      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
403      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
404      zvel[nSprite] = 0x44444;
405  }
406  
407  inline int thinkChaseGetTargetHeight(spritetype *pSprite, DUDEINFO *pDudeInfo, spritetype *pTarget)
408  {
409      if (VanillaMode() || !EnemiesNotBlood())
410          return 0;
411      DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type);
412      int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2;
413      int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2;
414      return height-height2;
415  }
416  
417  inline void thinkAirBrakes(int nSprite)
418  {
419      if (VanillaMode() || !EnemiesNotBlood() || !spriRangeIsFine(nSprite))
420          return;
421      xvel[nSprite] = -(xvel[nSprite]>>2);
422      yvel[nSprite] = -(yvel[nSprite]>>2);
423  }
424  
425  static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite)
426  {
427      if (pXSprite->target == -1)
428      {
429          aiNewState(pSprite, pXSprite, &gargoyleFGoto);
430          return;
431      }
432      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
433      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
434          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
435          return;
436      }
437      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
438      ///dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
439      if (!(pXSprite->target >= 0 && pXSprite->target < kMaxSprites)) {
440          consoleSysMsg("pXSprite->target >= 0 && pXSprite->target < kMaxSprites");
441          return;
442      }
443      spritetype *pTarget = &sprite[pXSprite->target];
444      XSPRITE *pXTarget = &xsprite[pTarget->extra];
445      int dx = pTarget->x-pSprite->x;
446      int dy = pTarget->y-pSprite->y;
447      aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
448      if (pXTarget->health == 0)
449      {
450          aiNewState(pSprite, pXSprite, &gargoyleFSearch);
451          return;
452      }
453      if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0)
454      {
455          aiNewState(pSprite, pXSprite, &gargoyleFSearch);
456          return;
457      }
458      int nDist = approxDist(dx, dy);
459      if (nDist <= pDudeInfo->seeDist)
460      {
461          int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
462          int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
463          // Should be dudeInfo[pTarget->type-kDudeBase]
464          int height2 = (pDudeInfo->eyeHeight*pTarget->yrepeat)<<2;
465          int top, bottom;
466          GetSpriteExtents(pSprite, &top, &bottom);
467          if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
468          {
469              if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
470              {
471                  aiSetTarget(pXSprite, pXSprite->target);
472                  int floorZ = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
473                  switch (pSprite->type) {
474                  case kDudeGargoyleFlesh:
475                      if (nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85)
476                      {
477                          int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget);
478                          int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0);
479                          switch (hit)
480                          {
481                          case -1:
482                              sfxPlay3DSound(pSprite, 1408, 0, 0);
483                              aiNewState(pSprite, pXSprite, &gargoyleFThrow);
484                              break;
485                          case 0:
486                          case 4:
487                              break;
488                          case 3:
489                              if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleStone)
490                              {
491                                  sfxPlay3DSound(pSprite, 1408, 0, 0);
492                                  aiNewState(pSprite, pXSprite, &gargoyleFThrow);
493                              }
494                              break;
495                          default:
496                              sfxPlay3DSound(pSprite, 1408, 0, 0);
497                              aiNewState(pSprite, pXSprite, &gargoyleFThrow);
498                              break;
499                          }
500                      }
501                      else if (nDist < 0x400 && klabs(nDeltaAngle) < 85)
502                      {
503                          int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget);
504                          int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0);
505                          switch (hit)
506                          {
507                          case -1:
508                              sfxPlay3DSound(pSprite, 1406, 0, 0);
509                              if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes
510                                  thinkAirBrakes(pSprite->index);
511                              aiNewState(pSprite, pXSprite, &gargoyleFSlash);
512                              break;
513                          case 0:
514                          case 4:
515                              break;
516                          case 3:
517                              if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleStone)
518                              {
519                                  sfxPlay3DSound(pSprite, 1406, 0, 0);
520                                  if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes
521                                      thinkAirBrakes(pSprite->index);
522                                  aiNewState(pSprite, pXSprite, &gargoyleFSlash);
523                              }
524                              break;
525                          default:
526                              sfxPlay3DSound(pSprite, 1406, 0, 0);
527                              if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes
528                                  thinkAirBrakes(pSprite->index);
529                              aiNewState(pSprite, pXSprite, &gargoyleFSlash);
530                              break;
531                          }
532                      }
533                      else if ((height2-height > 0x2000 || floorZ-bottom > 0x2000) && nDist < 0x1400 && nDist > 0xa00)
534                      {
535                          aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1);
536                          aiNewState(pSprite, pXSprite, &gargoyleSwoop);
537                      }
538                      else if ((height2-height < 0x2000 || floorZ-bottom < 0x2000) && klabs(nDeltaAngle) < 85)
539                          aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1);
540                      break;
541                  case kDudeGargoyleStone:
542                      if (nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85)
543                      {
544                          int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget);
545                          int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0);
546                          switch (hit)
547                          {
548                          case -1:
549                              sfxPlay3DSound(pSprite, 1457, 0, 0);
550                              aiNewState(pSprite, pXSprite, &gargoyleSBlast);
551                              break;
552                          case 0:
553                          case 4:
554                              break;
555                          case 3:
556                              if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleFlesh)
557                              {
558                                  sfxPlay3DSound(pSprite, 1457, 0, 0);
559                                  aiNewState(pSprite, pXSprite, &gargoyleSBlast);
560                              }
561                              break;
562                          default:
563                              sfxPlay3DSound(pSprite, 1457, 0, 0);
564                              aiNewState(pSprite, pXSprite, &gargoyleSBlast);
565                              break;
566                          }
567                      }
568                      else if (nDist < 0x400 && klabs(nDeltaAngle) < 85)
569                      {
570                          int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget);
571                          int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0);
572                          switch (hit)
573                          {
574                          case -1:
575                              if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes
576                                  thinkAirBrakes(pSprite->index);
577                              aiNewState(pSprite, pXSprite, &gargoyleFSlash);
578                              break;
579                          case 0:
580                          case 4:
581                              break;
582                          case 3:
583                              if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleFlesh)
584                              {
585                                  if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes
586                                      thinkAirBrakes(pSprite->index);
587                                  aiNewState(pSprite, pXSprite, &gargoyleFSlash);
588                              }
589                              break;
590                          default:
591                              if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes
592                                  thinkAirBrakes(pSprite->index);
593                              aiNewState(pSprite, pXSprite, &gargoyleFSlash);
594                              break;
595                          }
596                      }
597                      else if ((height2-height > 0x2000 || floorZ-bottom > 0x2000) && nDist < 0x1400 && nDist > 0x800)
598                      {
599                          if (pSprite->type == kDudeGargoyleFlesh)
600                              aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1);
601                          else
602                              aiPlay3DSound(pSprite, 1450, AI_SFX_PRIORITY_1, -1);
603                          aiNewState(pSprite, pXSprite, &gargoyleSwoop);
604                      }
605                      else if ((height2-height < 0x2000 || floorZ-bottom > 0x2000) && klabs(nDeltaAngle) < 85)
606                          aiPlay3DSound(pSprite, 1450, AI_SFX_PRIORITY_1, -1);
607                      break;
608                  }
609              }
610              return;
611          }
612          else
613          {
614              aiNewState(pSprite, pXSprite, &gargoyleFly);
615              return;
616          }
617      }
618  
619      aiNewState(pSprite, pXSprite, &gargoyleFGoto);
620      pXSprite->target = -1;
621  }
622  
623  static void entryFStatue(spritetype *pSprite, XSPRITE *pXSprite)
624  {
625      DUDEINFO *pDudeInfo = &dudeInfo[6];
626      actHealDude(pXSprite, pDudeInfo->startHealth, pDudeInfo->startHealth);
627      pSprite->type = kDudeGargoyleFlesh;
628  }
629  
630  static void entrySStatue(spritetype *pSprite, XSPRITE *pXSprite)
631  {
632      DUDEINFO *pDudeInfo = &dudeInfo[7];
633      actHealDude(pXSprite, pDudeInfo->startHealth, pDudeInfo->startHealth);
634      pSprite->type = kDudeGargoyleStone;
635  }
636  
637  static void MoveForward(spritetype *pSprite, XSPRITE *pXSprite)
638  {
639      int nSprite = pSprite->index;
640      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
641      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
642          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
643          return;
644      }
645      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
646      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
647      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
648      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
649      int nAccel = pDudeInfo->frontSpeed<<2;
650      if (klabs(nAng) > 341)
651          return;
652      if (pXSprite->target == -1)
653          pSprite->ang = (pSprite->ang+256)&2047;
654      int dx = pXSprite->targetX-pSprite->x;
655      int dy = pXSprite->targetY-pSprite->y;
656      int UNUSED(nAngle) = getangle(dx, dy);
657      int nDist = approxDist(dx, dy);
658      if ((unsigned int)Random(64) < 32 && nDist <= 0x400)
659          return;
660      int nCos = Cos(pSprite->ang);
661      int nSin = Sin(pSprite->ang);
662      int vx = xvel[nSprite];
663      int vy = yvel[nSprite];
664      int t1 = dmulscale30(vx, nCos, vy, nSin);
665      int t2 = dmulscale30(vx, nSin, -vy, nCos);
666      if (pXSprite->target == -1)
667          t1 += nAccel;
668      else
669          t1 += nAccel>>1;
670      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
671      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
672  }
673  
674  static void MoveSlow(spritetype *pSprite, XSPRITE *pXSprite)
675  {
676      int nSprite = pSprite->index;
677      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
678      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
679          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
680          return;
681      }
682      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
683      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
684      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
685      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
686      int nAccel = pDudeInfo->frontSpeed<<2;
687      if (klabs(nAng) > 341)
688      {
689          pXSprite->goalAng = (pSprite->ang+512)&2047;
690          return;
691      }
692      int dx = pXSprite->targetX-pSprite->x;
693      int dy = pXSprite->targetY-pSprite->y;
694      int UNUSED(nAngle) = getangle(dx, dy);
695      int nDist = approxDist(dx, dy);
696      if (Chance(0x600) && nDist <= 0x400)
697          return;
698      int nCos = Cos(pSprite->ang);
699      int nSin = Sin(pSprite->ang);
700      int vx = xvel[nSprite];
701      int vy = yvel[nSprite];
702      int t1 = dmulscale30(vx, nCos, vy, nSin);
703      int t2 = dmulscale30(vx, nSin, -vy, nCos);
704      t1 = nAccel>>1;
705      t2 >>= 1;
706      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
707      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
708      switch (pSprite->type) { 
709          case kDudeGargoyleFlesh:
710              zvel[nSprite] = 0x44444;
711              break;
712          case kDudeGargoyleStone:
713              zvel[nSprite] = 0x35555;
714              break;
715      }
716  }
717  
718  static void MoveSwoop(spritetype *pSprite, XSPRITE *pXSprite)
719  {
720      int nSprite = pSprite->index;
721      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
722      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
723          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
724          return;
725      }
726      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
727      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
728      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
729      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
730      int nAccel = pDudeInfo->frontSpeed<<2;
731      if (klabs(nAng) > 341)
732      {
733          pXSprite->goalAng = (pSprite->ang+512)&2047;
734          return;
735      }
736      int dx = pXSprite->targetX-pSprite->x;
737      int dy = pXSprite->targetY-pSprite->y;
738      int UNUSED(nAngle) = getangle(dx, dy);
739      int nDist = approxDist(dx, dy);
740      if (Chance(0x600) && nDist <= 0x400)
741          return;
742      int nCos = Cos(pSprite->ang);
743      int nSin = Sin(pSprite->ang);
744      int vx = xvel[nSprite];
745      int vy = yvel[nSprite];
746      int t1 = dmulscale30(vx, nCos, vy, nSin);
747      int t2 = dmulscale30(vx, nSin, -vy, nCos);
748      t1 += nAccel>>1;
749      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
750      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
751      switch (pSprite->type) {
752          case kDudeGargoyleFlesh:
753              zvel[nSprite] = t1;
754              break;
755          case kDudeGargoyleStone:
756              zvel[nSprite] = t1;
757              break;
758      }
759  }
760  
761  static void MoveFly(spritetype *pSprite, XSPRITE *pXSprite)
762  {
763      int nSprite = pSprite->index;
764      ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
765      if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
766          consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax");
767          return;
768      }
769      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
770      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
771      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
772      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
773      int nAccel = pDudeInfo->frontSpeed<<2;
774      if (klabs(nAng) > 341)
775      {
776          pSprite->ang = (pSprite->ang+512)&2047;
777          return;
778      }
779      int dx = pXSprite->targetX-pSprite->x;
780      int dy = pXSprite->targetY-pSprite->y;
781      int UNUSED(nAngle) = getangle(dx, dy);
782      int nDist = approxDist(dx, dy);
783      if (Chance(0x4000) && nDist <= 0x400)
784          return;
785      int nCos = Cos(pSprite->ang);
786      int nSin = Sin(pSprite->ang);
787      int vx = xvel[nSprite];
788      int vy = yvel[nSprite];
789      int t1 = dmulscale30(vx, nCos, vy, nSin);
790      int t2 = dmulscale30(vx, nSin, -vy, nCos);
791      t1 += nAccel>>1;
792      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
793      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
794      switch (pSprite->type) {
795          case kDudeGargoyleFlesh:
796              zvel[nSprite] = -t1;
797              break;
798          case kDudeGargoyleStone:
799              zvel[nSprite] = -t1;
800              break;
801      }
802      klabs(zvel[nSprite]);
803  }