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