/ source / blood / src / aiboneel.cpp
aiboneel.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 "build.h"
 24  #include "pragmas.h"
 25  #include "mmulti.h"
 26  #include "common_game.h"
 27  
 28  #include "actor.h"
 29  #include "ai.h"
 30  #include "aiboneel.h"
 31  #include "blood.h"
 32  #include "db.h"
 33  #include "dude.h"
 34  #include "levels.h"
 35  #include "player.h"
 36  #include "seq.h"
 37  #include "sfx.h"
 38  #include "trig.h"
 39  
 40  static void BiteSeqCallback(int, int);
 41  static void thinkTarget(spritetype *, XSPRITE *);
 42  static void thinkSearch(spritetype *, XSPRITE *);
 43  static void thinkGoto(spritetype *, XSPRITE *);
 44  static void thinkPonder(spritetype *, XSPRITE *);
 45  static void MoveDodgeUp(spritetype *, XSPRITE *);
 46  static void MoveDodgeDown(spritetype *, XSPRITE *);
 47  static void thinkChase(spritetype *, XSPRITE *);
 48  static void MoveForward(spritetype *, XSPRITE *);
 49  static void MoveSwoop(spritetype *, XSPRITE *);
 50  static void MoveAscend(spritetype *pSprite, XSPRITE *pXSprite);
 51  static void MoveToCeil(spritetype *, XSPRITE *);
 52  
 53  static int nBiteClient = seqRegisterClient(BiteSeqCallback);
 54  
 55  AISTATE eelIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, thinkTarget, NULL };
 56  AISTATE eelFlyIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, thinkTarget, NULL };
 57  AISTATE eelChase = { kAiStateChase, 0, -1, 0, NULL, MoveForward, thinkChase, &eelIdle };
 58  AISTATE eelPonder = { kAiStateOther, 0, -1, 0, NULL, NULL, thinkPonder, NULL };
 59  AISTATE eelGoto = { kAiStateMove, 0, -1, 600, NULL, NULL, thinkGoto, &eelIdle };
 60  AISTATE eelBite = { kAiStateChase, 7, nBiteClient, 60, NULL, NULL, NULL, &eelChase };
 61  AISTATE eelRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &eelChase };
 62  AISTATE eelSearch = { kAiStateSearch, 0, -1, 120, NULL, MoveForward, thinkSearch, &eelIdle };
 63  AISTATE eelSwoop = { kAiStateOther, 0, -1, 60, NULL, MoveSwoop, thinkChase, &eelChase };
 64  AISTATE eelFly = { kAiStateMove, 0, -1, 0, NULL, MoveAscend, thinkChase, &eelChase };
 65  AISTATE eelTurn = { kAiStateMove, 0, -1, 60, NULL, aiMoveTurn, NULL, &eelChase };
 66  AISTATE eelHide = { kAiStateOther, 0, -1, 0, NULL, MoveToCeil, MoveForward, NULL };
 67  AISTATE eelDodgeUp = { kAiStateMove, 0, -1, 120, NULL, MoveDodgeUp, NULL, &eelChase };
 68  AISTATE eelDodgeUpRight = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeUp, NULL, &eelChase };
 69  AISTATE eelDodgeUpLeft = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeUp, NULL, &eelChase };
 70  AISTATE eelDodgeDown = { kAiStateMove, 0, -1, 120, NULL, MoveDodgeDown, NULL, &eelChase };
 71  AISTATE eelDodgeDownRight = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeDown, NULL, &eelChase };
 72  AISTATE eelDodgeDownLeft = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeDown, NULL, &eelChase };
 73  
 74  static void BiteSeqCallback(int, int nXSprite)
 75  {
 76      XSPRITE *pXSprite = &xsprite[nXSprite];
 77      spritetype* pSprite = &sprite[pXSprite->reference];
 78      /*
 79       * workaround for
 80       * pXSprite->target >= 0 && pXSprite->target < kMaxSprites in file NBlood/source/blood/src/aiboneel.cpp at line 86
 81       * The value of pXSprite->target is -1.
 82       * copied from lines 177:181
 83       * resolves this case, but may cause other issues?
 84       */
 85      if (pXSprite->target == -1)
 86      {
 87          aiNewState(pSprite, pXSprite, &eelSearch);
 88          return;
 89      }
 90      
 91      spritetype *pTarget = &sprite[pXSprite->target];
 92      int dx = Cos(pSprite->ang) >> 16;
 93      int dy = Sin(pSprite->ang) >> 16;
 94      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
 95      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
 96      DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type);
 97      int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2;
 98      int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2;
 99  
100      dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
101      actFireVector(pSprite, 0, 0, dx, dy, height2-height, kVectorBoneelBite);
102  }
103  
104  static void thinkTarget(spritetype *pSprite, XSPRITE *pXSprite)
105  {
106      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
107      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
108      DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats;
109      if (pDudeExtraE->active && pDudeExtraE->thinkTime < 10)
110          pDudeExtraE->thinkTime++;
111      else if (pDudeExtraE->thinkTime >= 10 && pDudeExtraE->active)
112      {
113          pDudeExtraE->thinkTime = 0;
114          pXSprite->goalAng += 256;
115          POINT3D *pTarget = &baseSprite[pSprite->index];
116          aiSetTarget(pXSprite, pTarget->x, pTarget->y, pTarget->z);
117          aiNewState(pSprite, pXSprite, &eelTurn);
118          return;
119      }
120      if (Chance(pDudeInfo->alertChance))
121      {
122          for (int p = connecthead; p >= 0; p = connectpoint2[p])
123          {
124              PLAYER *pPlayer = &gPlayer[p];
125              if (pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0)
126                  continue;
127              int x = pPlayer->pSprite->x;
128              int y = pPlayer->pSprite->y;
129              int z = pPlayer->pSprite->z;
130              int nSector = pPlayer->pSprite->sectnum;
131              int dx = x-pSprite->x;
132              int dy = y-pSprite->y;
133              int nDist = approxDist(dx, dy);
134              if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist)
135                  continue;
136              if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum))
137                  continue;
138              int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
139              if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
140              {
141                  pDudeExtraE->thinkTime = 0;
142                  aiSetTarget(pXSprite, pPlayer->nSprite);
143                  aiActivateDude(pSprite, pXSprite);
144              }
145              else if (nDist < pDudeInfo->hearDist)
146              {
147                  pDudeExtraE->thinkTime = 0;
148                  aiSetTarget(pXSprite, x, y, z);
149                  aiActivateDude(pSprite, pXSprite);
150              }
151              else
152                  continue;
153              break;
154          }
155      }
156  }
157  
158  static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite)
159  {
160      aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
161      thinkTarget(pSprite, pXSprite);
162  }
163  
164  static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite)
165  {
166      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
167      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
168      int dx = pXSprite->targetX-pSprite->x;
169      int dy = pXSprite->targetY-pSprite->y;
170      int nAngle = getangle(dx, dy);
171      int nDist = approxDist(dx, dy);
172      aiChooseDirection(pSprite, pXSprite, nAngle);
173      if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
174          aiNewState(pSprite, pXSprite, &eelSearch);
175      thinkTarget(pSprite, pXSprite);
176  }
177  
178  static void thinkPonder(spritetype *pSprite, XSPRITE *pXSprite)
179  {
180      if (pXSprite->target == -1)
181      {
182          aiNewState(pSprite, pXSprite, &eelSearch);
183          return;
184      }
185      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
186      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
187      dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
188      spritetype *pTarget = &sprite[pXSprite->target];
189      XSPRITE *pXTarget = &xsprite[pTarget->extra];
190      int dx = pTarget->x-pSprite->x;
191      int dy = pTarget->y-pSprite->y;
192      aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
193      if (pXTarget->health == 0)
194      {
195          aiNewState(pSprite, pXSprite, &eelSearch);
196          return;
197      }
198      int nDist = approxDist(dx, dy);
199      if (nDist <= pDudeInfo->seeDist)
200      {
201          int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
202          int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
203          int height2 = (getDudeInfo(pTarget->type)->eyeHeight*pTarget->yrepeat)<<2;
204          int top, bottom;
205          GetSpriteExtents(pSprite, &top, &bottom);
206          if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
207          {
208              aiSetTarget(pXSprite, pXSprite->target);
209              if (height2-height < -0x2000 && nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85)
210                  aiNewState(pSprite, pXSprite, &eelDodgeUp);
211              else if (height2-height > 0xccc && nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85)
212                  aiNewState(pSprite, pXSprite, &eelDodgeDown);
213              else if (height2-height < 0xccc && nDist < 0x399 && klabs(nDeltaAngle) < 85)
214                  aiNewState(pSprite, pXSprite, &eelDodgeUp);
215              else if (height2-height > 0xccc && nDist < 0x1400 && nDist > 0x800 && klabs(nDeltaAngle) < 85)
216                  aiNewState(pSprite, pXSprite, &eelDodgeDown);
217              else if (height2-height < -0x2000 && nDist < 0x1400 && nDist > 0x800 && klabs(nDeltaAngle) < 85)
218                  aiNewState(pSprite, pXSprite, &eelDodgeUp);
219              else if (height2-height < -0x2000 && klabs(nDeltaAngle) < 85 && nDist > 0x1400)
220                  aiNewState(pSprite, pXSprite, &eelDodgeUp);
221              else if (height2-height > 0xccc)
222                  aiNewState(pSprite, pXSprite, &eelDodgeDown);
223              else
224                  aiNewState(pSprite, pXSprite, &eelDodgeUp);
225              return;
226          }
227      }
228      aiNewState(pSprite, pXSprite, &eelGoto);
229      pXSprite->target = -1;
230  }
231  
232  static void MoveDodgeUp(spritetype *pSprite, XSPRITE *pXSprite)
233  {
234      int nSprite = pSprite->index;
235      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
236      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
237      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
238      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
239      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
240      int nCos = Cos(pSprite->ang);
241      int nSin = Sin(pSprite->ang);
242      int dx = xvel[nSprite];
243      int dy = yvel[nSprite];
244      int t1 = dmulscale30(dx, nCos, dy, nSin);
245      int t2 = dmulscale30(dx, nSin, -dy, nCos);
246      if (pXSprite->dodgeDir > 0)
247          t2 += pDudeInfo->sideSpeed;
248      else
249          t2 -= pDudeInfo->sideSpeed;
250  
251      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
252      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
253      zvel[nSprite] = -0x8000;
254  }
255  
256  static void MoveDodgeDown(spritetype *pSprite, XSPRITE *pXSprite)
257  {
258      int nSprite = pSprite->index;
259      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
260      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
261      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
262      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
263      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
264      if (pXSprite->dodgeDir == 0)
265          return;
266      int nCos = Cos(pSprite->ang);
267      int nSin = Sin(pSprite->ang);
268      int dx = xvel[nSprite];
269      int dy = yvel[nSprite];
270      int t1 = dmulscale30(dx, nCos, dy, nSin);
271      int t2 = dmulscale30(dx, nSin, -dy, nCos);
272      if (pXSprite->dodgeDir > 0)
273          t2 += pDudeInfo->sideSpeed;
274      else
275          t2 -= pDudeInfo->sideSpeed;
276  
277      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
278      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
279      zvel[nSprite] = 0x44444;
280  }
281  
282  static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite)
283  {
284      if (pXSprite->target == -1)
285      {
286          aiNewState(pSprite, pXSprite, &eelGoto);
287          return;
288      }
289      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
290      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
291      dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
292      spritetype *pTarget = &sprite[pXSprite->target];
293      XSPRITE *pXTarget = &xsprite[pTarget->extra];
294      int dx = pTarget->x-pSprite->x;
295      int dy = pTarget->y-pSprite->y;
296      aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
297      if (pXTarget->health == 0)
298      {
299          aiNewState(pSprite, pXSprite, &eelSearch);
300          return;
301      }
302      if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0)
303      {
304          aiNewState(pSprite, pXSprite, &eelSearch);
305          return;
306      }
307      int nDist = approxDist(dx, dy);
308      if (nDist <= pDudeInfo->seeDist)
309      {
310          int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
311          int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
312          int top, bottom;
313          GetSpriteExtents(pSprite, &top, &bottom);
314          int top2, bottom2;
315          GetSpriteExtents(pTarget, &top2, &bottom2);
316          if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
317          {
318              if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
319              {
320                  aiSetTarget(pXSprite, pXSprite->target);
321                  if (nDist < 0x399 && top2 > top && klabs(nDeltaAngle) < 85)
322                      aiNewState(pSprite, pXSprite, &eelSwoop);
323                  else if (nDist <= 0x399 && klabs(nDeltaAngle) < 85)
324                      aiNewState(pSprite, pXSprite, &eelBite);
325                  else if (bottom2 > top && klabs(nDeltaAngle) < 85)
326                      aiNewState(pSprite, pXSprite, &eelSwoop);
327                  else if (top2 < top && klabs(nDeltaAngle) < 85)
328                      aiNewState(pSprite, pXSprite, &eelFly);
329              }
330          }
331          return;
332      }
333  
334      pXSprite->target = -1;
335      aiNewState(pSprite, pXSprite, &eelSearch);
336  }
337  
338  static void MoveForward(spritetype *pSprite, XSPRITE *pXSprite)
339  {
340      int nSprite = pSprite->index;
341      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
342      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
343      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
344      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
345      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
346      int nAccel = (pDudeInfo->frontSpeed-(((4-gGameOptions.nDifficulty)<<26)/120)/120)<<2;
347      if (klabs(nAng) > 341)
348          return;
349      if (pXSprite->target == -1)
350          pSprite->ang = (pSprite->ang+256)&2047;
351      int dx = pXSprite->targetX-pSprite->x;
352      int dy = pXSprite->targetY-pSprite->y;
353      int UNUSED(nAngle) = getangle(dx, dy);
354      int nDist = approxDist(dx, dy);
355      if (nDist <= 0x399)
356          return;
357      int nCos = Cos(pSprite->ang);
358      int nSin = Sin(pSprite->ang);
359      int vx = xvel[nSprite];
360      int vy = yvel[nSprite];
361      int t1 = dmulscale30(vx, nCos, vy, nSin);
362      int t2 = dmulscale30(vx, nSin, -vy, nCos);
363      if (pXSprite->target == -1)
364          t1 += nAccel;
365      else
366          t1 += nAccel>>1;
367      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
368      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
369  }
370  
371  static void MoveSwoop(spritetype *pSprite, XSPRITE *pXSprite)
372  {
373      int nSprite = pSprite->index;
374      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
375      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
376      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
377      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
378      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
379      int nAccel = (pDudeInfo->frontSpeed-(((4-gGameOptions.nDifficulty)<<26)/120)/120)<<2;
380      if (klabs(nAng) > 341)
381          return;
382      int dx = pXSprite->targetX-pSprite->x;
383      int dy = pXSprite->targetY-pSprite->y;
384      int UNUSED(nAngle) = getangle(dx, dy);
385      int nDist = approxDist(dx, dy);
386      if (Chance(0x8000) && nDist <= 0x399)
387          return;
388      int nCos = Cos(pSprite->ang);
389      int nSin = Sin(pSprite->ang);
390      int vx = xvel[nSprite];
391      int vy = yvel[nSprite];
392      int t1 = dmulscale30(vx, nCos, vy, nSin);
393      int t2 = dmulscale30(vx, nSin, -vy, nCos);
394      t1 += nAccel>>1;
395      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
396      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
397      zvel[nSprite] = 0x22222;
398  }
399  
400  static void MoveAscend(spritetype *pSprite, XSPRITE *pXSprite)
401  {
402      int nSprite = pSprite->index;
403      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
404      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
405      int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
406      int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
407      pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
408      int nAccel = (pDudeInfo->frontSpeed-(((4-gGameOptions.nDifficulty)<<26)/120)/120)<<2;
409      if (klabs(nAng) > 341)
410          return;
411      int dx = pXSprite->targetX-pSprite->x;
412      int dy = pXSprite->targetY-pSprite->y;
413      int UNUSED(nAngle) = getangle(dx, dy);
414      int nDist = approxDist(dx, dy);
415      if (Chance(0x4000) && nDist <= 0x399)
416          return;
417      int nCos = Cos(pSprite->ang);
418      int nSin = Sin(pSprite->ang);
419      int vx = xvel[nSprite];
420      int vy = yvel[nSprite];
421      int t1 = dmulscale30(vx, nCos, vy, nSin);
422      int t2 = dmulscale30(vx, nSin, -vy, nCos);
423      t1 += nAccel>>1;
424      xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
425      yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
426      zvel[nSprite] = -0x8000;
427  }
428  
429  void MoveToCeil(spritetype *pSprite, XSPRITE *pXSprite)
430  {
431      int x = pSprite->x;
432      int y = pSprite->y;
433      int z = pSprite->z;
434      int nSector = pSprite->sectnum;
435      if (z - pXSprite->targetZ < 0x1000)
436      {
437          DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats;
438          pDudeExtraE->active = 0;
439          pSprite->flags = 0;
440          aiNewState(pSprite, pXSprite, &eelIdle);
441      }
442      else
443          aiSetTarget(pXSprite, x, y, sector[nSector].ceilingz);
444  }