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