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