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