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 "aicaleb.h"
32 #include "blood.h"
33 #include "db.h"
34 #include "dude.h"
35 #include "levels.h"
36 #include "player.h"
37 #include "seq.h"
38 #include "sfx.h"
39 #include "trig.h"
40 
41 static void SeqAttackCallback(int, int);
42 static void thinkSearch(spritetype *, XSPRITE *);
43 static void thinkGoto(spritetype *, XSPRITE *);
44 static void thinkChase(spritetype *, XSPRITE *);
45 static void thinkSwimGoto(spritetype *, XSPRITE *);
46 static void thinkSwimChase(spritetype *, XSPRITE *);
47 static void sub_65D04(spritetype *, XSPRITE *);
48 static void sub_65F44(spritetype *, XSPRITE *);
49 static void sub_661E0(spritetype *, XSPRITE *);
50 
51 static int nAttackClient = seqRegisterClient(SeqAttackCallback);
52 
53 AISTATE tinycalebIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, aiThinkTarget, NULL };
54 AISTATE tinycalebChase = { kAiStateChase, 6, -1, 0, NULL, aiMoveForward, thinkChase, NULL };
55 AISTATE tinycalebDodge = { kAiStateMove, 6, -1, 90, NULL, aiMoveDodge, NULL, &tinycalebChase };
56 AISTATE tinycalebGoto = { kAiStateMove, 6, -1, 600, NULL, aiMoveForward, thinkGoto, &tinycalebIdle };
57 AISTATE tinycalebAttack = { kAiStateChase, 0, nAttackClient, 120, NULL, NULL, NULL, &tinycalebChase };
58 AISTATE tinycalebSearch = { kAiStateSearch, 6, -1, 120, NULL, aiMoveForward, thinkSearch, &tinycalebIdle };
59 AISTATE tinycalebRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &tinycalebDodge };
60 AISTATE tinycalebTeslaRecoil = { kAiStateRecoil, 4, -1, 0, NULL, NULL, NULL, &tinycalebDodge };
61 AISTATE tinycalebSwimIdle = { kAiStateIdle, 10, -1, 0, NULL, NULL, aiThinkTarget, NULL };
62 AISTATE tinycalebSwimChase = { kAiStateChase, 8, -1, 0, NULL, sub_65D04, thinkSwimChase, NULL };
63 AISTATE tinycalebSwimDodge = { kAiStateMove, 8, -1, 90, NULL, aiMoveDodge, NULL, &tinycalebSwimChase };
64 AISTATE tinycalebSwimGoto = { kAiStateMove, 8, -1, 600, NULL, aiMoveForward, thinkSwimGoto, &tinycalebSwimIdle };
65 AISTATE tinycalebSwimSearch = { kAiStateSearch, 8, -1, 120, NULL, aiMoveForward, thinkSearch, &tinycalebSwimIdle };
66 AISTATE tinycalebSwimAttack = { kAiStateChase, 10, nAttackClient, 0, NULL, NULL, NULL, &tinycalebSwimChase };
67 AISTATE tinycalebSwimRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &tinycalebSwimDodge };
68 AISTATE tinycaleb139660 = { kAiStateOther, 8, -1, 120, NULL, sub_65F44, thinkSwimChase, &tinycalebSwimChase };
69 AISTATE tinycaleb13967C = { kAiStateOther, 8, -1, 0, NULL, sub_661E0, thinkSwimChase, &tinycalebSwimChase };
70 AISTATE tinycaleb139698 = { kAiStateOther, 8, -1, 120, NULL, aiMoveTurn, NULL, &tinycalebSwimChase };
71 
SeqAttackCallback(int,int nXSprite)72 static void SeqAttackCallback(int, int nXSprite)
73 {
74     int nSprite = xsprite[nXSprite].reference;
75     spritetype *pSprite = &sprite[nSprite];
76     int dx = Cos(pSprite->ang)>>16;
77     int dy = Sin(pSprite->ang)>>16;
78     int dz = gDudeSlope[nXSprite];
79     dx += Random2(1500);
80     dy += Random2(1500);
81     dz += Random2(1500);
82     for (int i = 0; i < 2; i++)
83     {
84         int r1 = Random3(500);
85         int r2 = Random3(1000);
86         int r3 = Random3(1000);
87         actFireVector(pSprite, 0, 0, dx+r3, dy+r2, dz+r1, kVectorShell);
88     }
89     if (Chance(0x8000))
90         sfxPlay3DSound(pSprite, 10000+Random(5), -1, 0);
91     if (Chance(0x8000))
92         sfxPlay3DSound(pSprite, 1001, -1, 0);
93     else
94         sfxPlay3DSound(pSprite, 1002, -1, 0);
95 }
96 
thinkSearch(spritetype * pSprite,XSPRITE * pXSprite)97 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite)
98 {
99     aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
100     aiThinkTarget(pSprite, pXSprite);
101 }
102 
thinkGoto(spritetype * pSprite,XSPRITE * pXSprite)103 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite)
104 {
105     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
106     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
107     XSECTOR *pXSector;
108     int nXSector = sector[pSprite->sectnum].extra;
109     if (nXSector > 0)
110         pXSector = &xsector[nXSector];
111     else
112         pXSector = NULL;
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 < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
119     {
120         if (pXSector && pXSector->Underwater)
121             aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
122         else
123             aiNewState(pSprite, pXSprite, &tinycalebSearch);
124     }
125     aiThinkTarget(pSprite, pXSprite);
126 }
127 
thinkChase(spritetype * pSprite,XSPRITE * pXSprite)128 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite)
129 {
130     if (pXSprite->target == -1)
131     {
132         XSECTOR *pXSector;
133         int nXSector = sector[pSprite->sectnum].extra;
134         if (nXSector > 0)
135             pXSector = &xsector[nXSector];
136         else
137             pXSector = NULL;
138         if (pXSector && pXSector->Underwater)
139             aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
140         else
141             aiNewState(pSprite, pXSprite, &tinycalebSearch);
142         return;
143     }
144     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
145     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
146     dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
147     spritetype *pTarget = &sprite[pXSprite->target];
148     XSPRITE *pXTarget = &xsprite[pTarget->extra];
149     int dx = pTarget->x-pSprite->x;
150     int dy = pTarget->y-pSprite->y;
151     aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
152     if (pXTarget->health == 0)
153     {
154         XSECTOR *pXSector;
155         int nXSector = sector[pSprite->sectnum].extra;
156         if (nXSector > 0)
157             pXSector = &xsector[nXSector];
158         else
159             pXSector = NULL;
160         if (pXSector && pXSector->Underwater)
161             aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
162         else
163         {
164             aiPlay3DSound(pSprite, 11000+Random(4), AI_SFX_PRIORITY_1, -1);
165             aiNewState(pSprite, pXSprite, &tinycalebSearch);
166         }
167         return;
168     }
169     if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0)
170     {
171         XSECTOR *pXSector;
172         int nXSector = sector[pSprite->sectnum].extra;
173         if (nXSector > 0)
174             pXSector = &xsector[nXSector];
175         else
176             pXSector = NULL;
177         if (pXSector && pXSector->Underwater)
178             aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
179         else
180             aiNewState(pSprite, pXSprite, &tinycalebSearch);
181         return;
182     }
183     int nDist = approxDist(dx, dy);
184     if (nDist <= pDudeInfo->seeDist)
185     {
186         int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
187         int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
188         if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
189         {
190             if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
191             {
192                 aiSetTarget(pXSprite, pXSprite->target);
193                 int nXSprite = sprite[pXSprite->reference].extra;
194                 gDudeSlope[nXSprite] = divscale(pTarget->z-pSprite->z, nDist, 10);
195                 if (nDist < 0x599 && klabs(nDeltaAngle) < 28)
196                 {
197                     XSECTOR *pXSector;
198                     int nXSector = sector[pSprite->sectnum].extra;
199                     if (nXSector > 0)
200                         pXSector = &xsector[nXSector];
201                     else
202                         pXSector = NULL;
203                     int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
204                     switch (hit)
205                     {
206                     case -1:
207                         if (pXSector && pXSector->Underwater)
208                             aiNewState(pSprite, pXSprite, &tinycalebSwimAttack);
209                         else
210                             aiNewState(pSprite, pXSprite, &tinycalebAttack);
211                         break;
212                     case 3:
213                         if (pSprite->type != sprite[gHitInfo.hitsprite].type)
214                         {
215                             if (pXSector && pXSector->Underwater)
216                                 aiNewState(pSprite, pXSprite, &tinycalebSwimAttack);
217                             else
218                                 aiNewState(pSprite, pXSprite, &tinycalebAttack);
219                         }
220                         else
221                         {
222                             if (pXSector && pXSector->Underwater)
223                                 aiNewState(pSprite, pXSprite, &tinycalebSwimDodge);
224                             else
225                                 aiNewState(pSprite, pXSprite, &tinycalebDodge);
226                         }
227                         break;
228                     default:
229                         if (pXSector && pXSector->Underwater)
230                             aiNewState(pSprite, pXSprite, &tinycalebSwimAttack);
231                         else
232                             aiNewState(pSprite, pXSprite, &tinycalebAttack);
233                         break;
234                     }
235                 }
236             }
237             return;
238         }
239     }
240 
241     XSECTOR *pXSector;
242     int nXSector = sector[pSprite->sectnum].extra;
243     if (nXSector > 0)
244         pXSector = &xsector[nXSector];
245     else
246         pXSector = NULL;
247     if (pXSector && pXSector->Underwater)
248         aiNewState(pSprite, pXSprite, &tinycalebSwimGoto);
249     else
250         aiNewState(pSprite, pXSprite, &tinycalebGoto);
251     if (Chance(0x2000))
252         sfxPlay3DSound(pSprite, 10000 + Random(5), -1, 0);
253     pXSprite->target = -1;
254 }
255 
thinkSwimGoto(spritetype * pSprite,XSPRITE * pXSprite)256 static void thinkSwimGoto(spritetype *pSprite, XSPRITE *pXSprite)
257 {
258     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
259     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
260     int dx = pXSprite->targetX-pSprite->x;
261     int dy = pXSprite->targetY-pSprite->y;
262     int nAngle = getangle(dx, dy);
263     int nDist = approxDist(dx, dy);
264     aiChooseDirection(pSprite, pXSprite, nAngle);
265     if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
266         aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
267     aiThinkTarget(pSprite, pXSprite);
268 }
269 
thinkSwimChase(spritetype * pSprite,XSPRITE * pXSprite)270 static void thinkSwimChase(spritetype *pSprite, XSPRITE *pXSprite)
271 {
272     if (pXSprite->target == -1)
273     {
274         aiNewState(pSprite, pXSprite, &tinycalebSwimGoto);
275         return;
276     }
277     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
278     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
279     dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
280     spritetype *pTarget = &sprite[pXSprite->target];
281     XSPRITE *pXTarget = &xsprite[pTarget->extra];
282     int dx = pTarget->x-pSprite->x;
283     int dy = pTarget->y-pSprite->y;
284     aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
285     if (pXTarget->health == 0)
286     {
287         aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
288         return;
289     }
290     if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0)
291     {
292         aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
293         return;
294     }
295     int nDist = approxDist(dx, dy);
296     if (nDist <= pDudeInfo->seeDist)
297     {
298         int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
299         int height = pDudeInfo->eyeHeight+pSprite->z;
300         int top, bottom;
301         GetSpriteExtents(pSprite, &top, &bottom);
302         if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
303         {
304             if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
305             {
306                 aiSetTarget(pXSprite, pXSprite->target);
307                 int UNUSED(floorZ) = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
308                 if (nDist < 0x400 && klabs(nDeltaAngle) < 85)
309                     aiNewState(pSprite, pXSprite, &tinycalebSwimAttack);
310                 else
311                     aiNewState(pSprite, pXSprite, &tinycaleb13967C);
312             }
313         }
314         return;
315     }
316     aiNewState(pSprite, pXSprite, &tinycalebSwimGoto);
317     pXSprite->target = -1;
318 }
319 
sub_65D04(spritetype * pSprite,XSPRITE * pXSprite)320 static void sub_65D04(spritetype *pSprite, XSPRITE *pXSprite)
321 {
322     int nSprite = pSprite->index;
323     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
324     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
325     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
326     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
327     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
328     int nAccel = pDudeInfo->frontSpeed<<2;
329     if (klabs(nAng) > 341)
330         return;
331     if (pXSprite->target == -1)
332         pSprite->ang = (pSprite->ang+256)&2047;
333     int dx = pXSprite->targetX-pSprite->x;
334     int dy = pXSprite->targetY-pSprite->y;
335     int UNUSED(nAngle) = getangle(dx, dy);
336     int nDist = approxDist(dx, dy);
337     if (Random(64) < 32 && nDist <= 0x400)
338         return;
339     int nCos = Cos(pSprite->ang);
340     int nSin = Sin(pSprite->ang);
341     int vx = xvel[nSprite];
342     int vy = yvel[nSprite];
343     int t1 = dmulscale30(vx, nCos, vy, nSin);
344     int t2 = dmulscale30(vx, nSin, -vy, nCos);
345     if (pXSprite->target == -1)
346         t1 += nAccel;
347     else
348         t1 += nAccel>>2;
349     xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
350     yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
351 }
352 
sub_65F44(spritetype * pSprite,XSPRITE * pXSprite)353 static void sub_65F44(spritetype *pSprite, XSPRITE *pXSprite)
354 {
355     int nSprite = pSprite->index;
356     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
357     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
358     spritetype *pTarget = &sprite[pXSprite->target];
359     int z = pSprite->z + getDudeInfo(pSprite->type)->eyeHeight;
360     int z2 = pTarget->z + getDudeInfo(pTarget->type)->eyeHeight;
361     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
362     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
363     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
364     int nAccel = pDudeInfo->frontSpeed<<2;
365     if (klabs(nAng) > 341)
366     {
367         pXSprite->goalAng = (pSprite->ang+512)&2047;
368         return;
369     }
370     int dx = pXSprite->targetX-pSprite->x;
371     int dy = pXSprite->targetY-pSprite->y;
372     int dz = z2 - z;
373     int UNUSED(nAngle) = getangle(dx, dy);
374     int nDist = approxDist(dx, dy);
375     if (Chance(0x600) && nDist <= 0x400)
376         return;
377     int nCos = Cos(pSprite->ang);
378     int nSin = Sin(pSprite->ang);
379     int vx = xvel[nSprite];
380     int vy = yvel[nSprite];
381     int t1 = dmulscale30(vx, nCos, vy, nSin);
382     int t2 = dmulscale30(vx, nSin, -vy, nCos);
383     t1 += nAccel;
384     xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
385     yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
386     zvel[nSprite] = -dz;
387 }
388 
sub_661E0(spritetype * pSprite,XSPRITE * pXSprite)389 static void sub_661E0(spritetype *pSprite, XSPRITE *pXSprite)
390 {
391     int nSprite = pSprite->index;
392     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
393     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
394     spritetype *pTarget = &sprite[pXSprite->target];
395     int z = pSprite->z + getDudeInfo(pSprite->type)->eyeHeight;
396     int z2 = pTarget->z + getDudeInfo(pTarget->type)->eyeHeight;
397     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
398     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
399     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
400     int nAccel = pDudeInfo->frontSpeed<<2;
401     if (klabs(nAng) > 341)
402     {
403         pSprite->ang = (pSprite->ang+512)&2047;
404         return;
405     }
406     int dx = pXSprite->targetX-pSprite->x;
407     int dy = pXSprite->targetY-pSprite->y;
408     int dz = (z2 - z)<<3;
409     int UNUSED(nAngle) = getangle(dx, dy);
410     int nDist = approxDist(dx, dy);
411     if (Chance(0x4000) && nDist <= 0x400)
412         return;
413     int nCos = Cos(pSprite->ang);
414     int nSin = Sin(pSprite->ang);
415     int vx = xvel[nSprite];
416     int vy = yvel[nSprite];
417     int t1 = dmulscale30(vx, nCos, vy, nSin);
418     int t2 = dmulscale30(vx, nSin, -vy, nCos);
419     t1 += nAccel>>1;
420     xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
421     yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
422     zvel[nSprite] = dz;
423 }
424