/ source / blood / src / aizomba.cpp
aizomba.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 "aizomba.h"
 32  #include "blood.h"
 33  #include "db.h"
 34  #include "dude.h"
 35  #include "eventq.h"
 36  #include "levels.h"
 37  #include "player.h"
 38  #include "seq.h"
 39  #include "sfx.h"
 40  #include "trig.h"
 41  
 42  static void HackSeqCallback(int, int);
 43  static void StandSeqCallback(int, int);
 44  static void thinkSearch(spritetype *, XSPRITE *);
 45  static void thinkGoto(spritetype *, XSPRITE *);
 46  static void thinkChase(spritetype *, XSPRITE *);
 47  static void thinkPonder(spritetype *, XSPRITE *);
 48  static void myThinkTarget(spritetype *, XSPRITE *);
 49  static void myThinkSearch(spritetype *, XSPRITE *);
 50  static void entryEZombie(spritetype *, XSPRITE *);
 51  static void entryAIdle(spritetype *, XSPRITE *);
 52  static void entryEStand(spritetype *, XSPRITE *);
 53  
 54  static int nHackClient = seqRegisterClient(HackSeqCallback);
 55  static int nStandClient = seqRegisterClient(StandSeqCallback);
 56  
 57  AISTATE zombieAIdle = { kAiStateIdle, 0, -1, 0, entryAIdle, NULL, aiThinkTarget, NULL };
 58  AISTATE zombieAChase = { kAiStateChase, 8, -1, 0, NULL, aiMoveForward, thinkChase, NULL };
 59  AISTATE zombieAPonder = { kAiStateOther, 0, -1, 0, NULL, aiMoveTurn, thinkPonder, NULL };
 60  AISTATE zombieAGoto = { kAiStateMove, 8, -1, 1800, NULL, aiMoveForward, thinkGoto, &zombieAIdle };
 61  AISTATE zombieAHack = { kAiStateChase, 6, nHackClient, 80, NULL, NULL, NULL, &zombieAPonder };
 62  AISTATE zombieASearch = { kAiStateSearch, 8, -1, 1800, NULL, aiMoveForward, thinkSearch, &zombieAIdle };
 63  AISTATE zombieARecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &zombieAPonder };
 64  AISTATE zombieATeslaRecoil = { kAiStateRecoil, 4, -1, 0, NULL, NULL, NULL, &zombieAPonder };
 65  AISTATE zombieARecoil2 = { kAiStateRecoil, 1, -1, 360, NULL, NULL, NULL, &zombieAStand };
 66  AISTATE zombieAStand = { kAiStateMove, 11, nStandClient, 0, NULL, NULL, NULL, &zombieAPonder };
 67  AISTATE zombieEIdle = { kAiStateIdle, 12, -1, 0, NULL, NULL, aiThinkTarget, NULL };
 68  AISTATE zombieEUp2 = { kAiStateMove, 0, -1, 1, entryEZombie, NULL, NULL, &zombieASearch };
 69  AISTATE zombieEUp = { kAiStateMove, 9, -1, 180, entryEStand, NULL, NULL, &zombieEUp2 };
 70  AISTATE zombie2Idle = { kAiStateIdle, 0, -1, 0, entryAIdle, NULL, myThinkTarget, NULL };
 71  AISTATE zombie2Search = { kAiStateSearch, 8, -1, 1800, NULL, aiMoveForward, myThinkSearch, &zombie2Idle };
 72  AISTATE zombieSIdle = { kAiStateIdle, 10, -1, 0, NULL, NULL, aiThinkTarget, NULL };
 73  AISTATE zombie13AC2C = { kAiStateOther, 11, nStandClient, 0, entryEZombie, NULL, NULL, &zombieAPonder };
 74  
 75  static void HackSeqCallback(int, int nXSprite)
 76  {
 77      XSPRITE *pXSprite = &xsprite[nXSprite];
 78      int nSprite = pXSprite->reference;
 79      spritetype *pSprite = &sprite[nSprite];
 80      spritetype *pTarget = &sprite[pXSprite->target];
 81      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
 82      DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type);
 83      int tx = pXSprite->targetX-pSprite->x;
 84      int ty = pXSprite->targetY-pSprite->y;
 85      int UNUSED(nDist) = approxDist(tx, ty);
 86      int nAngle = getangle(tx, ty);
 87      int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2;
 88      int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2;
 89      int dz = height-height2;
 90      int dx = Cos(nAngle)>>16;
 91      int dy = Sin(nAngle)>>16;
 92      sfxPlay3DSound(pSprite, 1101, 1, 0);
 93      actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorAxe);
 94  }
 95  
 96  static void StandSeqCallback(int, int nXSprite)
 97  {
 98      XSPRITE *pXSprite = &xsprite[nXSprite];
 99      int nSprite = pXSprite->reference;
100      sfxPlay3DSound(&sprite[nSprite], 1102, -1, 0);
101  }
102  
103  static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite)
104  {
105      aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
106      aiLookForTarget(pSprite, pXSprite);
107  }
108  
109  static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite)
110  {
111      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
112      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
113      int dx = pXSprite->targetX-pSprite->x;
114      int dy = pXSprite->targetY-pSprite->y;
115      int nAngle = getangle(dx, dy);
116      int nDist = approxDist(dx, dy);
117      aiChooseDirection(pSprite, pXSprite, nAngle);
118      if (nDist < 921 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
119          aiNewState(pSprite, pXSprite, &zombieASearch);
120      aiThinkTarget(pSprite, pXSprite);
121  }
122  
123  static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite)
124  {
125      if (pXSprite->target == -1)
126      {
127          aiNewState(pSprite, pXSprite, &zombieASearch);
128          return;
129      }
130      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
131      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
132      dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
133      spritetype *pTarget = &sprite[pXSprite->target];
134      XSPRITE *pXTarget = &xsprite[pTarget->extra];
135      int dx = pTarget->x-pSprite->x;
136      int dy = pTarget->y-pSprite->y;
137      aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
138      if (pXTarget->health == 0)
139      {
140          aiNewState(pSprite, pXSprite, &zombieASearch);
141          return;
142      }
143      if (IsPlayerSprite(pTarget) && (powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0 || powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpDeathMaskUseless) > 0))
144      {
145          aiNewState(pSprite, pXSprite, &zombieAGoto);
146          return;
147      }
148      int nDist = approxDist(dx, dy);
149      if (nDist <= pDudeInfo->seeDist)
150      {
151          int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
152          int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
153          if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
154          {
155              if (klabs(nDeltaAngle) <= pDudeInfo->periphery)
156              {
157                  aiSetTarget(pXSprite, pXSprite->target);
158                  if (nDist < 0x400 && klabs(nDeltaAngle) < 85)
159                      aiNewState(pSprite, pXSprite, &zombieAHack);
160                  return;
161              }
162          }
163      }
164  
165      aiNewState(pSprite, pXSprite, &zombieAGoto);
166      pXSprite->target = -1;
167  }
168  
169  static void thinkPonder(spritetype *pSprite, XSPRITE *pXSprite)
170  {
171      if (pXSprite->target == -1)
172      {
173          aiNewState(pSprite, pXSprite, &zombieASearch);
174          return;
175      }
176      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
177      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
178      dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
179      spritetype *pTarget = &sprite[pXSprite->target];
180      if (!VanillaMode() && !xspriRangeIsFine(pTarget->extra))
181      {
182          aiNewState(pSprite, pXSprite, &zombieASearch);
183          return;
184      }
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, &zombieASearch);
192          return;
193      }
194      if (IsPlayerSprite(pTarget) && (powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0 || powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpDeathMaskUseless) > 0))
195      {
196          aiNewState(pSprite, pXSprite, &zombieAGoto);
197          return;
198      }
199      int nDist = approxDist(dx, dy);
200      if (nDist <= pDudeInfo->seeDist)
201      {
202          int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
203          int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
204          if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
205          {
206              if (klabs(nDeltaAngle) <= pDudeInfo->periphery)
207              {
208                  aiSetTarget(pXSprite, pXSprite->target);
209                  if (nDist < 0x400)
210                  {
211                      if (klabs(nDeltaAngle) < 85)
212                      {
213                          sfxPlay3DSound(pSprite, 1101, 1, 0);
214                          aiNewState(pSprite, pXSprite, &zombieAHack);
215                      }
216                      return;
217                  }
218              }
219          }
220      }
221  
222      aiNewState(pSprite, pXSprite, &zombieAChase);
223  }
224  
225  static void myThinkTarget(spritetype *pSprite, XSPRITE *pXSprite)
226  {
227      dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
228      DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
229      int nOwner = spriRangeIsFine(pSprite->owner) ? pSprite->owner : -1;
230      for (int p = connecthead; p >= 0; p = connectpoint2[p])
231      {
232          PLAYER *pPlayer = &gPlayer[p];
233          if (nOwner == pPlayer->nSprite || pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0)
234              continue;
235          int x = pPlayer->pSprite->x;
236          int y = pPlayer->pSprite->y;
237          int z = pPlayer->pSprite->z;
238          int nSector = pPlayer->pSprite->sectnum;
239          int dx = x-pSprite->x;
240          int dy = y-pSprite->y;
241          int nDist = approxDist(dx, dy);
242          if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist)
243              continue;
244          if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum))
245              continue;
246          int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
247          if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
248          {
249              aiSetTarget(pXSprite, pPlayer->nSprite);
250              aiActivateDude(pSprite, pXSprite);
251          }
252          else if (nDist < pDudeInfo->hearDist)
253          {
254              aiSetTarget(pXSprite, x, y, z);
255              aiActivateDude(pSprite, pXSprite);
256          }
257          else
258              continue;
259          break;
260      }
261  }
262  
263  static void myThinkSearch(spritetype *pSprite, XSPRITE *pXSprite)
264  {
265      aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
266      myThinkTarget(pSprite, pXSprite);
267  }
268  
269  static void entryEZombie(spritetype *pSprite, XSPRITE *pXSprite)
270  {
271      UNREFERENCED_PARAMETER(pXSprite);
272      pSprite->type = kDudeZombieAxeNormal;
273      pSprite->flags |= 1;
274  }
275  
276  static void entryAIdle(spritetype *pSprite, XSPRITE *pXSprite)
277  {
278      UNREFERENCED_PARAMETER(pSprite);
279      pXSprite->target = -1;
280  }
281  
282  static void entryEStand(spritetype *pSprite, XSPRITE *pXSprite)
283  {
284      sfxPlay3DSound(pSprite, 1100, -1, 0);
285      pSprite->ang = getangle(pXSprite->targetX-pSprite->x, pXSprite->targetY-pSprite->y);
286  }