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 "build.h"
24 #include "pragmas.h"
25 #include "mmulti.h"
26 #include "common_game.h"
27 #include "actor.h"
28 #include "ai.h"
29 #include "aibat.h"
30 #include "aibeast.h"
31 #include "aiboneel.h"
32 #include "aiburn.h"
33 #include "aicaleb.h"
34 #include "aicerber.h"
35 #include "aicult.h"
36 #include "aigarg.h"
37 #include "aighost.h"
38 #include "aigilbst.h"
39 #include "aihand.h"
40 #include "aihound.h"
41 #include "aiinnoc.h"
42 #include "aipod.h"
43 #include "airat.h"
44 #include "aispid.h"
45 #include "aitchern.h"
46 #include "aizomba.h"
47 #include "aizombf.h"
48 #ifdef NOONE_EXTENSIONS
49 #include "aiunicult.h"
50 #endif
51 #include "blood.h"
52 #include "db.h"
53 #include "dude.h"
54 #include "eventq.h"
55 #include "fx.h"
56 #include "gameutil.h"
57 #include "gib.h"
58 #include "globals.h"
59 #include "levels.h"
60 #include "loadsave.h"
61 #include "player.h"
62 #include "seq.h"
63 #include "sound.h"
64 #include "sfx.h"
65 #include "trig.h"
66 #include "triggers.h"
67 #include "view.h"
68 #ifdef NOONE_EXTENSIONS
69 #include "nnexts.h"
70 #endif
71 
72 int cumulDamage[kMaxXSprites];
73 int gDudeSlope[kMaxXSprites];
74 DUDEEXTRA gDudeExtra[kMaxXSprites];
75 
76 AISTATE genIdle = {kAiStateGenIdle, 0, -1, 0, NULL, NULL, NULL, NULL };
77 AISTATE genRecoil = {kAiStateRecoil, 5, -1, 20, NULL, NULL, NULL, &genIdle };
78 
79 int dword_138BB0[5] = {0x2000, 0x4000, 0x8000, 0xa000, 0xe000};
80 
sub_5BDA8(spritetype * pSprite,int nSeq)81 bool sub_5BDA8(spritetype *pSprite, int nSeq)
82 {
83     if (pSprite->statnum == kStatDude && pSprite->type >= kDudeBase && pSprite->type < kDudeMax)
84     {
85         DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
86         if (seqGetID(3, pSprite->extra) == pDudeInfo->seqStartID + nSeq && seqGetStatus(3, pSprite->extra) >= 0)
87             return true;
88     }
89     return false;
90 }
91 
aiPlay3DSound(spritetype * pSprite,int a2,AI_SFX_PRIORITY a3,int a4)92 void aiPlay3DSound(spritetype *pSprite, int a2, AI_SFX_PRIORITY a3, int a4)
93 {
94     DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra];
95     if (a3 == AI_SFX_PRIORITY_0)
96         sfxPlay3DSound(pSprite, a2, a4, 2);
97     else if (a3 > pDudeExtra->at5 || pDudeExtra->at0 <= (int)gFrameClock)
98     {
99         sfxKill3DSound(pSprite, -1, -1);
100         sfxPlay3DSound(pSprite, a2, a4, 0);
101         pDudeExtra->at5 = a3;
102         pDudeExtra->at0 = (int)gFrameClock+120;
103     }
104 }
105 
aiNewState(spritetype * pSprite,XSPRITE * pXSprite,AISTATE * pAIState)106 void aiNewState(spritetype *pSprite, XSPRITE *pXSprite, AISTATE *pAIState)
107 {
108     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
109     pXSprite->stateTimer = pAIState->stateTicks;
110     pXSprite->aiState = pAIState;
111     int seqStartId = pDudeInfo->seqStartID;
112 
113     if (pAIState->seqId >= 0) {
114         seqStartId += pAIState->seqId;
115         if (gSysRes.Lookup(seqStartId, "SEQ"))
116             seqSpawn(seqStartId, 3, pSprite->extra, pAIState->funcId);
117     }
118 
119     if (pAIState->enterFunc)
120         pAIState->enterFunc(pSprite, pXSprite);
121 }
122 
isImmune(spritetype * pSprite,int dmgType,int minScale)123 bool isImmune(spritetype* pSprite, int dmgType, int minScale) {
124 
125     if (dmgType >= kDmgFall && dmgType < kDmgMax && pSprite->extra >= 0 && xsprite[pSprite->extra].locked != 1) {
126         if (pSprite->type >= kThingBase && pSprite->type < kThingMax)
127             return (thingInfo[pSprite->type - kThingBase].dmgControl[dmgType] <= minScale);
128         else if (IsDudeSprite(pSprite)) {
129             if (IsPlayerSprite(pSprite)) return (gPlayer[pSprite->type - kDudePlayer1].damageControl[dmgType] <= minScale);
130             else return (dudeInfo[pSprite->type - kDudeBase].at70[dmgType] <= minScale);
131         }
132     }
133 
134     return true;
135 }
136 
CanMove(spritetype * pSprite,int a2,int nAngle,int nRange)137 bool CanMove(spritetype *pSprite, int a2, int nAngle, int nRange)
138 {
139     int top, bottom;
140     GetSpriteExtents(pSprite, &top, &bottom);
141     int x = pSprite->x;
142     int y = pSprite->y;
143     int z = pSprite->z;
144     HitScan(pSprite, z, Cos(nAngle)>>16, Sin(nAngle)>>16, 0, CLIPMASK0, nRange);
145     int nDist = approxDist(x-gHitInfo.hitx, y-gHitInfo.hity);
146     if (nDist - (pSprite->clipdist << 2) < nRange)
147     {
148         if (gHitInfo.hitsprite < 0 || a2 != gHitInfo.hitsprite)
149             return false;
150         return true;
151     }
152     x += mulscale30(nRange, Cos(nAngle));
153     y += mulscale30(nRange, Sin(nAngle));
154     int nSector = pSprite->sectnum;
155     dassert(nSector >= 0 && nSector < kMaxSectors);
156     if (!FindSector(x, y, z, &nSector))
157         return false;
158     int floorZ = getflorzofslope(nSector, x, y);
159     int UNUSED(ceilZ) = getceilzofslope(nSector, x, y);
160     int nXSector = sector[nSector].extra;
161     char Underwater = 0; char Water = 0; char Depth = 0; char Crusher = 0;
162     XSECTOR* pXSector = NULL;
163     if (nXSector > 0)
164     {
165         pXSector = &xsector[nXSector];
166         if (pXSector->Underwater)
167             Underwater = 1;
168         if (pXSector->Depth)
169             Depth = 1;
170         if (sector[nSector].type == kSectorDamage || pXSector->damageType > 0)
171             Crusher = 1;
172     }
173     int nUpper = gUpperLink[nSector];
174     int nLower = gLowerLink[nSector];
175     if (nUpper >= 0)
176     {
177         if (sprite[nUpper].type == kMarkerUpWater || sprite[nUpper].type == kMarkerUpGoo)
178             Water = Depth = 1;
179     }
180     if (nLower >= 0)
181     {
182         if (sprite[nLower].type == kMarkerLowWater || sprite[nLower].type == kMarkerLowGoo)
183             Depth = 1;
184     }
185     switch (pSprite->type) {
186     case kDudeGargoyleFlesh:
187     case kDudeGargoyleStone:
188     case kDudeBat:
189         if (pSprite->clipdist > nDist)
190             return 0;
191         if (Depth)
192         {
193             // Ouch...
194             if (Depth)
195                 return false;
196             if (Crusher)
197                 return false;
198         }
199         break;
200     case kDudeBoneEel:
201         if (Water)
202             return false;
203         if (!Underwater)
204             return false;
205         if (Underwater)
206             return true;
207         break;
208     case kDudeCerberusTwoHead:
209     case kDudeCerberusOneHead:
210         // by NoOne: a quick fix for Cerberus spinning in E3M7-like maps, where damage sectors is used.
211         // It makes ignore danger if enemy immune to N damageType. As result Cerberus start acting like
212         // in Blood 1.0 so it can move normally to player. It's up to you for adding rest of enemies here as
213         // i don't think it will broke something in game.
214         if (!VanillaMode() && Crusher && isImmune(pSprite, pXSector->damageType, 16)) return true;
215         fallthrough__;
216     case kDudeZombieButcher:
217     case kDudeSpiderBrown:
218     case kDudeSpiderRed:
219     case kDudeSpiderBlack:
220     case kDudeSpiderMother:
221     case kDudeHellHound:
222     case kDudeRat:
223     case kDudeInnocent:
224         if (Crusher)
225             return false;
226         if (Depth || Underwater)
227             return false;
228         if (floorZ - bottom > 0x2000)
229             return false;
230         break;
231     #ifdef NOONE_EXTENSIONS
232     case kDudeModernCustom:
233     case kDudeModernCustomBurning:
234         if ((Crusher && !nnExtIsImmune(pSprite, pXSector->damageType)) || ((Water || Underwater) && !canSwim(pSprite))) return false;
235         return true;
236         fallthrough__;
237     #endif
238     case kDudeZombieAxeNormal:
239     case kDudePhantasm:
240     case kDudeGillBeast:
241     default:
242         if (Crusher)
243             return false;
244         if ((nXSector < 0 || (!xsector[nXSector].Underwater && !xsector[nXSector].Depth)) && floorZ - bottom > 0x2000)
245             return false;
246         break;
247     }
248     return 1;
249 }
250 
aiChooseDirection(spritetype * pSprite,XSPRITE * pXSprite,int a3)251 void aiChooseDirection(spritetype *pSprite, XSPRITE *pXSprite, int a3)
252 {
253     int nSprite = pSprite->index;
254     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
255     int vc = ((a3+1024-pSprite->ang)&2047)-1024;
256     int nCos = Cos(pSprite->ang);
257     int nSin = Sin(pSprite->ang);
258     int dx = xvel[nSprite];
259     int dy = yvel[nSprite];
260     int t1 = dmulscale30(dx, nCos, dy, nSin);
261     int UNUSED(t2) = dmulscale30(dx, nSin, -dy, nCos);
262     int vsi = ((t1*15)>>12) / 2;
263     int v8 = 341;
264     if (vc < 0)
265         v8 = -341;
266     if (CanMove(pSprite, pXSprite->target, pSprite->ang+vc, vsi))
267         pXSprite->goalAng = pSprite->ang+vc;
268     else if (CanMove(pSprite, pXSprite->target, pSprite->ang+vc/2, vsi))
269         pXSprite->goalAng = pSprite->ang+vc/2;
270     else if (CanMove(pSprite, pXSprite->target, pSprite->ang-vc/2, vsi))
271         pXSprite->goalAng = pSprite->ang-vc/2;
272     else if (CanMove(pSprite, pXSprite->target, pSprite->ang+v8, vsi))
273         pXSprite->goalAng = pSprite->ang+v8;
274     else if (CanMove(pSprite, pXSprite->target, pSprite->ang, vsi))
275         pXSprite->goalAng = pSprite->ang;
276     else if (CanMove(pSprite, pXSprite->target, pSprite->ang-v8, vsi))
277         pXSprite->goalAng = pSprite->ang-v8;
278     //else if (pSprite->flags&2)
279         //pXSprite->goalAng = pSprite->ang+341;
280     else // Weird..
281         pXSprite->goalAng = pSprite->ang+341;
282     if (Chance(0x8000))
283         pXSprite->dodgeDir = 1;
284     else
285         pXSprite->dodgeDir = -1;
286     if (!CanMove(pSprite, pXSprite->target, pSprite->ang+pXSprite->dodgeDir*512, 512))
287     {
288         pXSprite->dodgeDir = -pXSprite->dodgeDir;
289         if (!CanMove(pSprite, pXSprite->target, pSprite->ang+pXSprite->dodgeDir*512, 512))
290             pXSprite->dodgeDir = 0;
291     }
292 }
293 
aiMoveForward(spritetype * pSprite,XSPRITE * pXSprite)294 void aiMoveForward(spritetype *pSprite, XSPRITE *pXSprite)
295 {
296     int nSprite = pSprite->index;
297     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
298     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
299     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
300     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
301     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
302     if (klabs(nAng) > 341)
303         return;
304     xvel[nSprite] += mulscale30(pDudeInfo->frontSpeed, Cos(pSprite->ang));
305     yvel[nSprite] += mulscale30(pDudeInfo->frontSpeed, Sin(pSprite->ang));
306 }
307 
aiMoveTurn(spritetype * pSprite,XSPRITE * pXSprite)308 void aiMoveTurn(spritetype *pSprite, XSPRITE *pXSprite)
309 {
310     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
311     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
312     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
313     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
314     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
315 }
316 
aiMoveDodge(spritetype * pSprite,XSPRITE * pXSprite)317 void aiMoveDodge(spritetype *pSprite, XSPRITE *pXSprite)
318 {
319     int nSprite = pSprite->index;
320     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
321     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
322     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
323     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
324     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
325     if (pXSprite->dodgeDir)
326     {
327         int nCos = Cos(pSprite->ang);
328         int nSin = Sin(pSprite->ang);
329         int dx = xvel[nSprite];
330         int dy = yvel[nSprite];
331         int t1 = dmulscale30(dx, nCos, dy, nSin);
332         int t2 = dmulscale30(dx, nSin, -dy, nCos);
333         if (pXSprite->dodgeDir > 0)
334             t2 += pDudeInfo->sideSpeed;
335         else
336             t2 -= pDudeInfo->sideSpeed;
337 
338         xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
339         yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
340     }
341 }
342 
aiActivateDude(spritetype * pSprite,XSPRITE * pXSprite)343 void aiActivateDude(spritetype *pSprite, XSPRITE *pXSprite)
344 {
345     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
346     if (!pXSprite->state) {
347         aiChooseDirection(pSprite, pXSprite, getangle(pXSprite->targetX-pSprite->x, pXSprite->targetY-pSprite->y));
348         pXSprite->state = 1;
349     }
350     switch (pSprite->type) {
351     case kDudePhantasm:
352     {
353         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
354         pDudeExtraE->at4 = 0;
355         pDudeExtraE->at8 = 1;
356         pDudeExtraE->at0 = 0;
357         if (pXSprite->target == -1)
358             aiNewState(pSprite, pXSprite, &ghostSearch);
359         else
360         {
361             aiPlay3DSound(pSprite, 1600, AI_SFX_PRIORITY_1, -1);
362             aiNewState(pSprite, pXSprite, &ghostChase);
363         }
364         break;
365     }
366     case kDudeCultistTommy:
367     case kDudeCultistShotgun:
368     case kDudeCultistTesla:
369     case kDudeCultistTNT:
370     case kDudeCultistBeast:
371     {
372         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
373         pDudeExtraE->at8 = 1;
374         pDudeExtraE->at0 = 0;
375         if (pXSprite->target == -1) {
376             switch (pXSprite->medium) {
377                 case kMediumNormal:
378                     aiNewState(pSprite, pXSprite, &cultistSearch);
379                     if (Chance(0x8000)) {
380                         if (pSprite->type == kDudeCultistTommy) aiPlay3DSound(pSprite, 4008+Random(5), AI_SFX_PRIORITY_1, -1);
381                         else aiPlay3DSound(pSprite, 1008+Random(5), AI_SFX_PRIORITY_1, -1);
382                     }
383                     break;
384                 case kMediumWater:
385                 case kMediumGoo:
386                     aiNewState(pSprite, pXSprite, &cultistSwimSearch);
387                     break;
388             }
389         } else {
390             if (Chance(0x8000)) {
391                 if (pSprite->type == kDudeCultistTommy) aiPlay3DSound(pSprite, 4003+Random(4), AI_SFX_PRIORITY_1, -1);
392                 else aiPlay3DSound(pSprite, 1003+Random(4), AI_SFX_PRIORITY_1, -1);
393             }
394             switch (pXSprite->medium) {
395                 case kMediumNormal:
396                     if (pSprite->type == kDudeCultistTommy) aiNewState(pSprite, pXSprite, &fanaticChase);
397                     else aiNewState(pSprite, pXSprite, &cultistChase);
398                     break;
399                 case kMediumWater:
400                 case kMediumGoo:
401                     aiNewState(pSprite, pXSprite, &cultistSwimChase);
402                     break;
403             }
404         }
405         break;
406     }
407 #ifdef NOONE_EXTENSIONS
408     case kDudeModernCustom:
409     {
410         DUDEEXTRA_at6_u1* pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
411         pDudeExtraE->at8 = 1;
412         pDudeExtraE->at0 = 0;
413         if (pXSprite->target == -1) {
414             if (spriteIsUnderwater(pSprite, false))  aiGenDudeNewState(pSprite, &genDudeSearchW);
415             else aiGenDudeNewState(pSprite, &genDudeSearchL);
416         } else {
417             if (Chance(0x4000)) playGenDudeSound(pSprite, kGenDudeSndTargetSpot);
418             if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeChaseW);
419             else aiGenDudeNewState(pSprite, &genDudeChaseL);
420         }
421         break;
422     }
423     case kDudeModernCustomBurning:
424         if (pXSprite->target == -1) aiGenDudeNewState(pSprite, &genDudeBurnSearch);
425         else aiGenDudeNewState(pSprite, &genDudeBurnChase);
426     break;
427 #endif
428     case kDudeCultistTommyProne: {
429         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
430         pDudeExtraE->at8 = 1; pDudeExtraE->at0 = 0;
431         pSprite->type = kDudeCultistTommy;
432         if (pXSprite->target == -1) {
433             switch (pXSprite->medium) {
434                 case 0:
435                     aiNewState(pSprite, pXSprite, &cultistSearch);
436                     if (Chance(0x8000))
437                         aiPlay3DSound(pSprite, 4008+Random(5), AI_SFX_PRIORITY_1, -1);
438                     break;
439                 case kMediumWater:
440                 case kMediumGoo:
441                     aiNewState(pSprite, pXSprite, &cultistSwimSearch);
442                     break;
443             }
444         } else {
445             if (Chance(0x8000))
446                 aiPlay3DSound(pSprite, 4008+Random(5), AI_SFX_PRIORITY_1, -1);
447 
448             switch (pXSprite->medium) {
449                 case kMediumNormal:
450                     aiNewState(pSprite, pXSprite, &cultistProneChase);
451                     break;
452                 case kMediumWater:
453                 case kMediumGoo:
454                     aiNewState(pSprite, pXSprite, &cultistSwimChase);
455                     break;
456             }
457         }
458         break;
459     }
460     case kDudeCultistShotgunProne:
461     {
462         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
463         pDudeExtraE->at8 = 1;
464         pDudeExtraE->at0 = 0;
465         pSprite->type = kDudeCultistShotgun;
466         if (pXSprite->target == -1)
467         {
468             switch (pXSprite->medium)
469             {
470             case kMediumNormal:
471                 aiNewState(pSprite, pXSprite, &cultistSearch);
472                 if (Chance(0x8000))
473                     aiPlay3DSound(pSprite, 1008+Random(5), AI_SFX_PRIORITY_1, -1);
474                 break;
475             case kMediumWater:
476             case kMediumGoo:
477                 aiNewState(pSprite, pXSprite, &cultistSwimSearch);
478                 break;
479             }
480         }
481         else
482         {
483             if (Chance(0x8000))
484                 aiPlay3DSound(pSprite, 1003+Random(4), AI_SFX_PRIORITY_1, -1);
485             switch (pXSprite->medium)
486             {
487             case kMediumNormal:
488                 aiNewState(pSprite, pXSprite, &cultistProneChase);
489                 break;
490             case kMediumWater:
491             case kMediumGoo:
492                 aiNewState(pSprite, pXSprite, &cultistSwimChase);
493                 break;
494             }
495         }
496         break;
497     }
498     case kDudeBurningCultist:
499         if (pXSprite->target == -1)
500             aiNewState(pSprite, pXSprite, &cultistBurnSearch);
501         else
502             aiNewState(pSprite, pXSprite, &cultistBurnChase);
503         break;
504     case kDudeBat:
505     {
506         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
507         pDudeExtraE->at4 = 0;
508         pDudeExtraE->at8 = 1;
509         pDudeExtraE->at0 = 0;
510         if (!pSprite->flags)
511             pSprite->flags = 9;
512         if (pXSprite->target == -1)
513             aiNewState(pSprite, pXSprite, &batSearch);
514         else
515         {
516             if (Chance(0xa000))
517                 aiPlay3DSound(pSprite, 2000, AI_SFX_PRIORITY_1, -1);
518             aiNewState(pSprite, pXSprite, &batChase);
519         }
520         break;
521     }
522     case kDudeBoneEel:
523     {
524         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
525         pDudeExtraE->at4 = 0;
526         pDudeExtraE->at8 = 1;
527         pDudeExtraE->at0 = 0;
528         if (pXSprite->target == -1)
529             aiNewState(pSprite, pXSprite, &eelSearch);
530         else
531         {
532             if (Chance(0x8000))
533                 aiPlay3DSound(pSprite, 1501, AI_SFX_PRIORITY_1, -1);
534             else
535                 aiPlay3DSound(pSprite, 1500, AI_SFX_PRIORITY_1, -1);
536             aiNewState(pSprite, pXSprite, &eelChase);
537         }
538         break;
539     }
540     case kDudeGillBeast: {
541         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
542         XSECTOR *pXSector = NULL;
543         if (sector[pSprite->sectnum].extra > 0)
544             pXSector = &xsector[sector[pSprite->sectnum].extra];
545         pDudeExtraE->at0 = 0;
546         pDudeExtraE->at4 = 0;
547         pDudeExtraE->at8 = 1;
548         if (pXSprite->target == -1)
549         {
550             if (pXSector && pXSector->Underwater)
551                 aiNewState(pSprite, pXSprite, &gillBeastSwimSearch);
552             else
553                 aiNewState(pSprite, pXSprite, &gillBeastSearch);
554         }
555         else
556         {
557             if (Chance(0x4000))
558                 aiPlay3DSound(pSprite, 1701, AI_SFX_PRIORITY_1, -1);
559             else
560                 aiPlay3DSound(pSprite, 1700, AI_SFX_PRIORITY_1, -1);
561             if (pXSector && pXSector->Underwater)
562                 aiNewState(pSprite, pXSprite, &gillBeastSwimChase);
563             else
564                 aiNewState(pSprite, pXSprite, &gillBeastChase);
565         }
566         break;
567     }
568     case kDudeZombieAxeNormal: {
569         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2;
570         pDudeExtraE->at4 = 1;
571         pDudeExtraE->at0 = 0;
572         if (pXSprite->target == -1)
573             aiNewState(pSprite, pXSprite, &zombieASearch);
574         else
575         {
576             if (Chance(0xa000))
577             {
578                 switch (Random(3))
579                 {
580                 default:
581                 case 0:
582                 case 3:
583                     aiPlay3DSound(pSprite, 1103, AI_SFX_PRIORITY_1, -1);
584                     break;
585                 case 1:
586                     aiPlay3DSound(pSprite, 1104, AI_SFX_PRIORITY_1, -1);
587                     break;
588                 case 2:
589                     aiPlay3DSound(pSprite, 1105, AI_SFX_PRIORITY_1, -1);
590                     break;
591                 }
592             }
593             aiNewState(pSprite, pXSprite, &zombieAChase);
594         }
595         break;
596     }
597     case kDudeZombieAxeBuried:
598     {
599         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2;
600         pDudeExtraE->at4 = 1;
601         pDudeExtraE->at0 = 0;
602         if (pXSprite->aiState == &zombieEIdle)
603             aiNewState(pSprite, pXSprite, &zombieEUp);
604         break;
605     }
606     case kDudeZombieAxeLaying:
607     {
608         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2;
609         pDudeExtraE->at4 = 1;
610         pDudeExtraE->at0 = 0;
611         if (pXSprite->aiState == &zombieSIdle)
612             aiNewState(pSprite, pXSprite, &zombie13AC2C);
613         break;
614     }
615     case kDudeZombieButcher: {
616         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2;
617         pDudeExtraE->at4 = 1;
618         pDudeExtraE->at0 = 0;
619         if (pXSprite->target == -1)
620             aiNewState(pSprite, pXSprite, &zombieFSearch);
621         else
622         {
623             if (Chance(0x4000))
624                 aiPlay3DSound(pSprite, 1201, AI_SFX_PRIORITY_1, -1);
625             else
626                 aiPlay3DSound(pSprite, 1200, AI_SFX_PRIORITY_1, -1);
627             aiNewState(pSprite, pXSprite, &zombieFChase);
628         }
629         break;
630     }
631     case kDudeBurningZombieAxe:
632         if (pXSprite->target == -1)
633             aiNewState(pSprite, pXSprite, &zombieABurnSearch);
634         else
635             aiNewState(pSprite, pXSprite, &zombieABurnChase);
636         break;
637     case kDudeBurningZombieButcher:
638         if (pXSprite->target == -1)
639             aiNewState(pSprite, pXSprite, &zombieFBurnSearch);
640         else
641             aiNewState(pSprite, pXSprite, &zombieFBurnChase);
642         break;
643     case kDudeGargoyleFlesh: {
644         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
645         pDudeExtraE->at4 = 0;
646         pDudeExtraE->at8 = 1;
647         pDudeExtraE->at0 = 0;
648         if (pXSprite->target == -1)
649             aiNewState(pSprite, pXSprite, &gargoyleFSearch);
650         else
651         {
652             if (Chance(0x4000))
653                 aiPlay3DSound(pSprite, 1401, AI_SFX_PRIORITY_1, -1);
654             else
655                 aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1);
656             aiNewState(pSprite, pXSprite, &gargoyleFChase);
657         }
658         break;
659     }
660     case kDudeGargoyleStone:
661     {
662         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
663         pDudeExtraE->at4 = 0;
664         pDudeExtraE->at8 = 1;
665         pDudeExtraE->at0 = 0;
666         if (pXSprite->target == -1)
667             aiNewState(pSprite, pXSprite, &gargoyleFSearch);
668         else
669         {
670             if (Chance(0x4000))
671                 aiPlay3DSound(pSprite, 1451, AI_SFX_PRIORITY_1, -1);
672             else
673                 aiPlay3DSound(pSprite, 1450, AI_SFX_PRIORITY_1, -1);
674             aiNewState(pSprite, pXSprite, &gargoyleFChase);
675         }
676         break;
677     }
678     case kDudeGargoyleStatueFlesh:
679     case kDudeGargoyleStatueStone:
680 
681         #ifdef NOONE_EXTENSIONS
682         // play gargoyle statue breaking animation if data1 = 1.
683         if (gModernMap && pXSprite->data1 == 1) {
684             if (pSprite->type == kDudeGargoyleStatueFlesh) aiNewState(pSprite, pXSprite, &statueFBreakSEQ);
685             else aiNewState(pSprite, pXSprite, &statueSBreakSEQ);
686         } else {
687             if (Chance(0x4000)) aiPlay3DSound(pSprite, 1401, AI_SFX_PRIORITY_1, -1);
688             else aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1);
689 
690             if (pSprite->type == kDudeGargoyleStatueFlesh) aiNewState(pSprite, pXSprite, &gargoyleFMorph);
691             else aiNewState(pSprite, pXSprite, &gargoyleSMorph);
692         }
693         #else
694         if (Chance(0x4000)) aiPlay3DSound(pSprite, 1401, AI_SFX_PRIORITY_1, -1);
695         else aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1);
696 
697         if (pSprite->type == kDudeGargoyleStatueFlesh) aiNewState(pSprite, pXSprite, &gargoyleFMorph);
698         else aiNewState(pSprite, pXSprite, &gargoyleSMorph);
699         #endif
700         break;
701     case kDudeCerberusTwoHead:
702         if (pXSprite->target == -1)
703             aiNewState(pSprite, pXSprite, &cerberusSearch);
704         else
705         {
706             aiPlay3DSound(pSprite, 2300, AI_SFX_PRIORITY_1, -1);
707             aiNewState(pSprite, pXSprite, &cerberusChase);
708         }
709         break;
710     case kDudeCerberusOneHead:
711         if (pXSprite->target == -1)
712             aiNewState(pSprite, pXSprite, &cerberus2Search);
713         else
714         {
715             aiPlay3DSound(pSprite, 2300, AI_SFX_PRIORITY_1, -1);
716             aiNewState(pSprite, pXSprite, &cerberus2Chase);
717         }
718         break;
719     case kDudeHellHound:
720         if (pXSprite->target == -1)
721             aiNewState(pSprite, pXSprite, &houndSearch);
722         else
723         {
724             aiPlay3DSound(pSprite, 1300, AI_SFX_PRIORITY_1, -1);
725             aiNewState(pSprite, pXSprite, &houndChase);
726         }
727         break;
728     case kDudeHand:
729         if (pXSprite->target == -1)
730             aiNewState(pSprite, pXSprite, &handSearch);
731         else
732         {
733             aiPlay3DSound(pSprite, 1900, AI_SFX_PRIORITY_1, -1);
734             aiNewState(pSprite, pXSprite, &handChase);
735         }
736         break;
737     case kDudeRat:
738         if (pXSprite->target == -1)
739             aiNewState(pSprite, pXSprite, &ratSearch);
740         else
741         {
742             aiPlay3DSound(pSprite, 2100, AI_SFX_PRIORITY_1, -1);
743             aiNewState(pSprite, pXSprite, &ratChase);
744         }
745         break;
746     case kDudeInnocent:
747         if (pXSprite->target == -1)
748             aiNewState(pSprite, pXSprite, &innocentSearch);
749         else
750         {
751             if (pXSprite->health > 0)
752                 aiPlay3DSound(pSprite, 7000+Random(6), AI_SFX_PRIORITY_1, -1);
753             aiNewState(pSprite, pXSprite, &innocentChase);
754         }
755         break;
756     case kDudeTchernobog:
757         if (pXSprite->target == -1)
758             aiNewState(pSprite, pXSprite, &tchernobogSearch);
759         else
760         {
761             aiPlay3DSound(pSprite, 2350+Random(7), AI_SFX_PRIORITY_1, -1);
762             aiNewState(pSprite, pXSprite, &tchernobogChase);
763         }
764         break;
765     case kDudeSpiderBrown:
766     case kDudeSpiderRed:
767     case kDudeSpiderBlack:
768         pSprite->flags |= 2;
769         pSprite->cstat &= ~8;
770         if (pXSprite->target == -1)
771             aiNewState(pSprite, pXSprite, &spidSearch);
772         else
773         {
774             aiPlay3DSound(pSprite, 1800, AI_SFX_PRIORITY_1, -1);
775             aiNewState(pSprite, pXSprite, &spidChase);
776         }
777         break;
778     case kDudeSpiderMother: {
779         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u1;
780         pDudeExtraE->at8 = 1;
781         pDudeExtraE->at0 = 0;
782         pSprite->flags |= 2;
783         pSprite->cstat &= ~8;
784         if (pXSprite->target == -1)
785             aiNewState(pSprite, pXSprite, &spidSearch);
786         else
787         {
788             aiPlay3DSound(pSprite, 1853+Random(1), AI_SFX_PRIORITY_1, -1);
789             aiNewState(pSprite, pXSprite, &spidChase);
790         }
791         break;
792     }
793     case kDudeTinyCaleb:
794     {
795         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2;
796         pDudeExtraE->at4 = 1;
797         pDudeExtraE->at0 = 0;
798         if (pXSprite->target == -1)
799         {
800             switch (pXSprite->medium)
801             {
802             case kMediumNormal:
803                 aiNewState(pSprite, pXSprite, &tinycalebSearch);
804                 break;
805             case kMediumWater:
806             case kMediumGoo:
807                 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch);
808                 break;
809             }
810         }
811         else
812         {
813             switch (pXSprite->medium)
814             {
815             case kMediumNormal:
816                 aiNewState(pSprite, pXSprite, &tinycalebChase);
817                 break;
818             case kMediumWater:
819             case kMediumGoo:
820                 aiNewState(pSprite, pXSprite, &tinycalebSwimChase);
821                 break;
822             }
823         }
824         break;
825     }
826     case kDudeBeast:
827     {
828         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[pSprite->extra].at6.u2;
829         pDudeExtraE->at4 = 1;
830         pDudeExtraE->at0 = 0;
831         if (pXSprite->target == -1)
832         {
833             switch (pXSprite->medium)
834             {
835             case kMediumNormal:
836                 aiNewState(pSprite, pXSprite, &beastSearch);
837                 break;
838             case kMediumWater:
839             case kMediumGoo:
840                 aiNewState(pSprite, pXSprite, &beastSwimSearch);
841                 break;
842             }
843         }
844         else
845         {
846             aiPlay3DSound(pSprite, 9009+Random(2), AI_SFX_PRIORITY_1, -1);
847             switch (pXSprite->medium)
848             {
849             case kMediumNormal:
850                 aiNewState(pSprite, pXSprite, &beastChase);
851                 break;
852             case kMediumWater:
853             case kMediumGoo:
854                 aiNewState(pSprite, pXSprite, &beastSwimChase);
855                 break;
856             }
857         }
858         break;
859     }
860     case kDudePodGreen:
861     case kDudePodFire:
862         if (pXSprite->target == -1)
863             aiNewState(pSprite, pXSprite, &podSearch);
864         else
865         {
866             if (pSprite->type == kDudePodFire)
867                 aiPlay3DSound(pSprite, 2453, AI_SFX_PRIORITY_1, -1);
868             else
869                 aiPlay3DSound(pSprite, 2473, AI_SFX_PRIORITY_1, -1);
870             aiNewState(pSprite, pXSprite, &podChase);
871         }
872         break;
873     case kDudeTentacleGreen:
874     case kDudeTentacleFire:
875         if (pXSprite->target == -1)
876             aiNewState(pSprite, pXSprite, &tentacleSearch);
877         else
878         {
879             aiPlay3DSound(pSprite, 2503, AI_SFX_PRIORITY_1, -1);
880             aiNewState(pSprite, pXSprite, &tentacleChase);
881         }
882         break;
883     }
884 }
885 
aiSetTarget(XSPRITE * pXSprite,int x,int y,int z)886 void aiSetTarget(XSPRITE *pXSprite, int x, int y, int z)
887 {
888     pXSprite->target = -1;
889     pXSprite->targetX = x;
890     pXSprite->targetY = y;
891     pXSprite->targetZ = z;
892 }
893 
aiSetTarget(XSPRITE * pXSprite,int nTarget)894 void aiSetTarget(XSPRITE *pXSprite, int nTarget)
895 {
896     dassert(nTarget >= 0 && nTarget < kMaxSprites);
897     spritetype *pTarget = &sprite[nTarget];
898     if (pTarget->type >= kDudeBase && pTarget->type < kDudeMax)
899     {
900         if (actSpriteOwnerToSpriteId(&sprite[pXSprite->reference]) != nTarget)
901         {
902             pXSprite->target = nTarget;
903             DUDEINFO *pDudeInfo = getDudeInfo(pTarget->type);
904             pXSprite->targetX = pTarget->x;
905             pXSprite->targetY = pTarget->y;
906             pXSprite->targetZ = pTarget->z-((pDudeInfo->eyeHeight*pTarget->yrepeat)<<2);
907         }
908     }
909 }
910 
911 
aiDamageSprite(spritetype * pSprite,XSPRITE * pXSprite,int nSource,DAMAGE_TYPE nDmgType,int nDamage)912 int aiDamageSprite(spritetype *pSprite, XSPRITE *pXSprite, int nSource, DAMAGE_TYPE nDmgType, int nDamage)
913 {
914     dassert(nSource < kMaxSprites);
915     if (!pXSprite->health)
916         return 0;
917     pXSprite->health = ClipLow(pXSprite->health - nDamage, 0);
918     cumulDamage[pSprite->extra] += nDamage;
919     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
920     int nSprite = pXSprite->reference;
921     if (nSource >= 0) {
922         spritetype *pSource = &sprite[nSource];
923         if (pSprite == pSource) return 0;
924         else if (pXSprite->target == -1 || (nSource != pXSprite->target && Chance(pSprite->type == pSource->type ? nDamage*pDudeInfo->changeTargetKin : nDamage*pDudeInfo->changeTarget)))
925         {
926             aiSetTarget(pXSprite, nSource);
927             aiActivateDude(pSprite, pXSprite);
928         }
929 
930         #ifdef NOONE_EXTENSIONS
931         if (gModernMap) {
932 
933             // for enemies in patrol mode
934             if (aiInPatrolState(pXSprite->aiState)) {
935 
936                 aiPatrolStop(pSprite, pSource->index, pXSprite->dudeAmbush);
937 
938                 PLAYER* pPlayer = getPlayerById(pSource->type);
939                 if (!pPlayer) return nDamage;
940                 if (powerupCheck(pPlayer, kPwUpShadowCloak)) pPlayer->pwUpTime[kPwUpShadowCloak] = 0;
941                 if (readyForCrit(pSource, pSprite)) {
942                     nDamage += aiDamageSprite(pSprite, pXSprite, pSource->index, nDmgType, nDamage * (10 - gGameOptions.nDifficulty));
943                     if (pXSprite->health > 0) {
944                         int fullHp = (pXSprite->sysData2 > 0) ? ClipRange(pXSprite->sysData2 << 4, 1, 65535) : getDudeInfo(pSprite->type)->startHealth << 4;
945                         if (((100 * pXSprite->health) / fullHp) <= 75) {
946                             cumulDamage[pSprite->extra] += nDamage << 4; // to be sure any enemy will play the recoil animation
947                             RecoilDude(pSprite, pXSprite);
948                         }
949                     }
950 
951                     consoleSysMsg("Player #%d does the critical damage to patrol dude #%d!", pPlayer->nPlayer + 1, pSprite->index);
952                 }
953 
954                 return nDamage;
955             }
956 
957             if (pSprite->type == kDudeModernCustomBurning) {
958 
959                 if (Chance(0x2000) && gDudeExtra[pSprite->extra].at0 < (int)gFrameClock) {
960                     playGenDudeSound(pSprite, kGenDudeSndBurning);
961                     gDudeExtra[pSprite->extra].at0 = (int)gFrameClock + 360;
962                 }
963 
964                 if (pXSprite->burnTime == 0) pXSprite->burnTime = 2400;
965                 if (spriteIsUnderwater(pSprite, false)) {
966                     pSprite->type = kDudeModernCustom;
967                     pXSprite->burnTime = 0;
968                     pXSprite->health = 1; // so it can be killed with flame weapons while underwater and if already was burning dude before.
969                     aiGenDudeNewState(pSprite, &genDudeGotoW);
970                 }
971 
972                 return nDamage;
973 
974             }
975 
976             if (pSprite->type == kDudeModernCustom) {
977 
978                 GENDUDEEXTRA* pExtra = genDudeExtra(pSprite);
979                 if (nDmgType == kDamageBurn) {
980 
981                     if (pXSprite->health > pDudeInfo->fleeHealth) return nDamage;
982                     else if (pXSprite->txID <= 0 || getNextIncarnation(pXSprite) == NULL) {
983                         removeDudeStuff(pSprite);
984 
985                         if (pExtra->weaponType == kGenDudeWeaponKamikaze)
986                             doExplosion(pSprite, pXSprite->data1 - kTrapExploder);
987 
988                         if (spriteIsUnderwater(pSprite)) {
989                             pXSprite->health = 0;
990                             return nDamage;
991                         }
992 
993                         if (pXSprite->burnTime <= 0)
994                             pXSprite->burnTime = 1200;
995 
996                         if (pExtra->canBurn && pExtra->availDeaths[kDamageBurn] > 0) {
997 
998                             aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
999                             playGenDudeSound(pSprite, kGenDudeSndBurning);
1000                             pSprite->type = kDudeModernCustomBurning;
1001 
1002                             if (pXSprite->data2 == kGenDudeDefaultSeq) // don't inherit palette for burning if using default animation
1003                                 pSprite->pal = 0;
1004 
1005                             aiGenDudeNewState(pSprite, &genDudeBurnGoto);
1006                             actHealDude(pXSprite, dudeInfo[55].startHealth, dudeInfo[55].startHealth);
1007                             gDudeExtra[pSprite->extra].at0 = (int)gFrameClock + 360;
1008                             evKill(nSprite, 3, kCallbackFXFlameLick);
1009 
1010                         }
1011 
1012                     } else {
1013                         actKillDude(nSource, pSprite, kDamageFall, 65535);
1014                     }
1015 
1016                 } else if (canWalk(pSprite) && !inDodge(pXSprite->aiState) && !inRecoil(pXSprite->aiState)) {
1017 
1018                     if (!dudeIsMelee(pXSprite)) {
1019                         if (inIdle(pXSprite->aiState) || Chance(getDodgeChance(pSprite))) {
1020                             if (!spriteIsUnderwater(pSprite)) {
1021                                 if (!canDuck(pSprite) || !sub_5BDA8(pSprite, 14))  aiGenDudeNewState(pSprite, &genDudeDodgeShortL);
1022                                 else aiGenDudeNewState(pSprite, &genDudeDodgeShortD);
1023 
1024                                 if (Chance(0x0200))
1025                                     playGenDudeSound(pSprite, kGenDudeSndGotHit);
1026 
1027                             } else if (sub_5BDA8(pSprite, 13)) {
1028                                 aiGenDudeNewState(pSprite, &genDudeDodgeShortW);
1029                             }
1030                         }
1031                     } else if (Chance(0x0200)) {
1032                         playGenDudeSound(pSprite, kGenDudeSndGotHit);
1033                     }
1034 
1035                 }
1036 
1037                 return nDamage;
1038 
1039             }
1040         }
1041         #endif
1042 
1043         if (nDmgType == kDamageTesla)
1044         {
1045             DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra];
1046             pDudeExtra->at4 = 1;
1047         }
1048         switch (pSprite->type)
1049         {
1050         case kDudeCultistTommy:
1051         case kDudeCultistShotgun:
1052         case kDudeCultistTesla:
1053         case kDudeCultistTNT:
1054             if (nDmgType != kDamageBurn)
1055             {
1056                 if (!sub_5BDA8(pSprite, 14) && !pXSprite->medium)
1057                     aiNewState(pSprite, pXSprite, &cultistDodge);
1058                 else if (sub_5BDA8(pSprite, 14) && !pXSprite->medium)
1059                     aiNewState(pSprite, pXSprite, &cultistProneDodge);
1060                 else if (sub_5BDA8(pSprite, 13) && (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo))
1061                     aiNewState(pSprite, pXSprite, &cultistSwimDodge);
1062             }
1063             else if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth/* && (pXSprite->at17_6 != 1 || pXSprite->at17_6 != 2)*/)
1064             {
1065                 pSprite->type = kDudeBurningCultist;
1066                 aiNewState(pSprite, pXSprite, &cultistBurnGoto);
1067                 aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
1068                 aiPlay3DSound(pSprite, 1031+Random(2), AI_SFX_PRIORITY_2, -1);
1069                 gDudeExtra[pSprite->extra].at0 = (int)gFrameClock+360;
1070                 actHealDude(pXSprite, dudeInfo[40].startHealth, dudeInfo[40].startHealth);
1071                 evKill(nSprite, 3, kCallbackFXFlameLick);
1072             }
1073             break;
1074         case kDudeInnocent:
1075             if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth/* && (pXSprite->at17_6 != 1 || pXSprite->at17_6 != 2)*/)
1076             {
1077                 pSprite->type = kDudeBurningInnocent;
1078                 aiNewState(pSprite, pXSprite, &cultistBurnGoto);
1079                 aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
1080                 gDudeExtra[pSprite->extra].at0 = (int)gFrameClock+360;
1081                 actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
1082                 evKill(nSprite, 3, kCallbackFXFlameLick);
1083             }
1084             break;
1085         case kDudeBurningCultist:
1086             if (Chance(0x4000) && gDudeExtra[pSprite->extra].at0 < gFrameClock)
1087             {
1088                 aiPlay3DSound(pSprite, 1031+Random(2), AI_SFX_PRIORITY_2, -1);
1089                 gDudeExtra[pSprite->extra].at0 = (int)gFrameClock+360;
1090             }
1091             if (Chance(0x600) && (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo))
1092             {
1093                 pSprite->type = kDudeCultistTommy;
1094                 pXSprite->burnTime = 0;
1095                 aiNewState(pSprite, pXSprite, &cultistSwimGoto);
1096             }
1097             else if (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo)
1098             {
1099                 pSprite->type = kDudeCultistShotgun;
1100                 pXSprite->burnTime = 0;
1101                 aiNewState(pSprite, pXSprite, &cultistSwimGoto);
1102             }
1103             break;
1104         case kDudeGargoyleFlesh:
1105             aiNewState(pSprite, pXSprite, &gargoyleFChase);
1106             break;
1107         case kDudeZombieButcher:
1108             if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth) {
1109                 aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
1110                 aiPlay3DSound(pSprite, 1202, AI_SFX_PRIORITY_2, -1);
1111                 pSprite->type = kDudeBurningZombieButcher;
1112                 aiNewState(pSprite, pXSprite, &zombieFBurnGoto);
1113                 actHealDude(pXSprite, dudeInfo[42].startHealth, dudeInfo[42].startHealth);
1114                 evKill(nSprite, 3, kCallbackFXFlameLick);
1115             }
1116             break;
1117         case kDudeTinyCaleb:
1118             if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth/* && (pXSprite->at17_6 != 1 || pXSprite->at17_6 != 2)*/)
1119             {
1120                 pSprite->type = kDudeBurningInnocent;
1121                 aiNewState(pSprite, pXSprite, &cultistBurnGoto);
1122                 aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
1123                 gDudeExtra[pSprite->extra].at0 = (int)gFrameClock+360;
1124                 actHealDude(pXSprite, dudeInfo[39].startHealth, dudeInfo[39].startHealth);
1125                 evKill(nSprite, 3, kCallbackFXFlameLick);
1126             }
1127             break;
1128         case kDudeCultistBeast:
1129             if (pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth)
1130             {
1131                 pSprite->type = kDudeBeast;
1132                 aiPlay3DSound(pSprite, 9008, AI_SFX_PRIORITY_1, -1);
1133                 aiNewState(pSprite, pXSprite, &beastMorphFromCultist);
1134                 actHealDude(pXSprite, dudeInfo[51].startHealth, dudeInfo[51].startHealth);
1135             }
1136             break;
1137         case kDudeZombieAxeNormal:
1138         case kDudeZombieAxeBuried:
1139             if (nDmgType == kDamageBurn && pXSprite->health <= (unsigned int)pDudeInfo->fleeHealth)
1140             {
1141                 aiPlay3DSound(pSprite, 361, AI_SFX_PRIORITY_0, -1);
1142                 aiPlay3DSound(pSprite, 1106, AI_SFX_PRIORITY_2, -1);
1143                 pSprite->type = kDudeBurningZombieAxe;
1144                 aiNewState(pSprite, pXSprite, &zombieABurnGoto);
1145                 actHealDude(pXSprite, dudeInfo[41].startHealth, dudeInfo[41].startHealth);
1146                 evKill(nSprite, 3, kCallbackFXFlameLick);
1147             }
1148             break;
1149         }
1150     }
1151     return nDamage;
1152 }
1153 
RecoilDude(spritetype * pSprite,XSPRITE * pXSprite)1154 void RecoilDude(spritetype *pSprite, XSPRITE *pXSprite)
1155 {
1156     char v4 = Chance(0x8000);
1157     DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra];
1158     if (pSprite->statnum == kStatDude && (pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) {
1159         DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
1160         switch (pSprite->type) {
1161 #ifdef NOONE_EXTENSIONS
1162         case kDudeModernCustom: {
1163             GENDUDEEXTRA* pExtra = genDudeExtra(pSprite); int rChance = getRecoilChance(pSprite);
1164             if (pExtra->canElectrocute && pDudeExtra->at4 && !spriteIsUnderwater(pSprite, false)) {
1165 
1166                 if (Chance(rChance << 3) || (dudeIsMelee(pXSprite) && Chance(rChance << 4))) aiGenDudeNewState(pSprite, &genDudeRecoilTesla);
1167                 else if (pExtra->canRecoil && Chance(rChance)) aiGenDudeNewState(pSprite, &genDudeRecoilL);
1168                 else if (canWalk(pSprite)) {
1169 
1170                     if (Chance(rChance >> 2)) aiGenDudeNewState(pSprite, &genDudeDodgeL);
1171                     else if (Chance(rChance >> 1)) aiGenDudeNewState(pSprite, &genDudeDodgeShortL);
1172 
1173                 }
1174 
1175             } else if (pExtra->canRecoil && Chance(rChance)) {
1176 
1177                 if (inDuck(pXSprite->aiState) && Chance(rChance >> 2)) aiGenDudeNewState(pSprite, &genDudeRecoilD);
1178                 else if (spriteIsUnderwater(pSprite, false)) aiGenDudeNewState(pSprite, &genDudeRecoilW);
1179                 else aiGenDudeNewState(pSprite, &genDudeRecoilL);
1180 
1181             }
1182 
1183             short rState = inRecoil(pXSprite->aiState);
1184             if (rState > 0) {
1185 
1186                 if (!canWalk(pSprite)) {
1187                     if (rState == 1) pXSprite->aiState->nextState = &genDudeChaseNoWalkL;
1188                     else if (rState == 2) pXSprite->aiState->nextState = &genDudeChaseNoWalkD;
1189                     else pXSprite->aiState->nextState = &genDudeChaseNoWalkW;
1190 
1191                 } else if (!dudeIsMelee(pXSprite) || Chance(rChance >> 2)) {
1192                     if (rState == 1) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeL : &genDudeDodgeShortL);
1193                     else if (rState == 2) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeD : &genDudeDodgeShortD);
1194                     else if (rState == 3) pXSprite->aiState->nextState = (Chance(rChance) ? &genDudeDodgeW : &genDudeDodgeShortW);
1195 
1196                 }
1197                 else if (rState == 1) pXSprite->aiState->nextState = &genDudeChaseL;
1198                 else if (rState == 2) pXSprite->aiState->nextState = &genDudeChaseD;
1199                 else pXSprite->aiState->nextState = &genDudeChaseW;
1200 
1201                 playGenDudeSound(pSprite, kGenDudeSndGotHit);
1202 
1203             }
1204 
1205             pDudeExtra->at4 = 0;
1206             break;
1207         }
1208 #endif
1209         case kDudeCultistTommy:
1210         case kDudeCultistShotgun:
1211         case kDudeCultistTesla:
1212         case kDudeCultistTNT:
1213         case kDudeCultistBeast:
1214             if (pSprite->type == kDudeCultistTommy) aiPlay3DSound(pSprite, 4013+Random(2), AI_SFX_PRIORITY_2, -1);
1215             else aiPlay3DSound(pSprite, 1013+Random(2), AI_SFX_PRIORITY_2, -1);
1216 
1217             if (!v4 && pXSprite->medium == kMediumNormal) {
1218                 if (pDudeExtra->at4) aiNewState(pSprite, pXSprite, &cultistTeslaRecoil);
1219                 else aiNewState(pSprite, pXSprite, &cultistRecoil);
1220 
1221             } else if (v4 && pXSprite->medium == kMediumNormal) {
1222                 if (pDudeExtra->at4) aiNewState(pSprite, pXSprite, &cultistTeslaRecoil);
1223                 else if (gGameOptions.nDifficulty > 0) aiNewState(pSprite, pXSprite, &cultistProneRecoil);
1224                 else aiNewState(pSprite, pXSprite, &cultistRecoil);
1225             }
1226             else if (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo)
1227                 aiNewState(pSprite, pXSprite, &cultistSwimRecoil);
1228             else
1229             {
1230                 if (pDudeExtra->at4)
1231                     aiNewState(pSprite, pXSprite, &cultistTeslaRecoil);
1232                 else
1233                     aiNewState(pSprite, pXSprite, &cultistRecoil);
1234             }
1235             break;
1236         case kDudeBurningCultist:
1237             aiNewState(pSprite, pXSprite, &cultistBurnGoto);
1238             break;
1239 #ifdef NOONE_EXTENSIONS
1240         case kDudeModernCustomBurning:
1241             aiGenDudeNewState(pSprite, &genDudeBurnGoto);
1242             break;
1243 #endif
1244         case kDudeZombieButcher:
1245             aiPlay3DSound(pSprite, 1202, AI_SFX_PRIORITY_2, -1);
1246             if (pDudeExtra->at4)
1247                 aiNewState(pSprite, pXSprite, &zombieFTeslaRecoil);
1248             else
1249                 aiNewState(pSprite, pXSprite, &zombieFRecoil);
1250             break;
1251         case kDudeZombieAxeNormal:
1252         case kDudeZombieAxeBuried:
1253             aiPlay3DSound(pSprite, 1106, AI_SFX_PRIORITY_2, -1);
1254             if (pDudeExtra->at4 && pXSprite->data3 > pDudeInfo->startHealth/3)
1255                 aiNewState(pSprite, pXSprite, &zombieATeslaRecoil);
1256             else if (pXSprite->data3 > pDudeInfo->startHealth/3)
1257                 aiNewState(pSprite, pXSprite, &zombieARecoil2);
1258             else
1259                 aiNewState(pSprite, pXSprite, &zombieARecoil);
1260             break;
1261         case kDudeBurningZombieAxe:
1262             aiPlay3DSound(pSprite, 1106, AI_SFX_PRIORITY_2, -1);
1263             aiNewState(pSprite, pXSprite, &zombieABurnGoto);
1264             break;
1265         case kDudeBurningZombieButcher:
1266             aiPlay3DSound(pSprite, 1202, AI_SFX_PRIORITY_2, -1);
1267             aiNewState(pSprite, pXSprite, &zombieFBurnGoto);
1268             break;
1269         case kDudeGargoyleFlesh:
1270         case kDudeGargoyleStone:
1271             aiPlay3DSound(pSprite, 1402, AI_SFX_PRIORITY_2, -1);
1272             aiNewState(pSprite, pXSprite, &gargoyleFRecoil);
1273             break;
1274         case kDudeCerberusTwoHead:
1275             aiPlay3DSound(pSprite, 2302+Random(2), AI_SFX_PRIORITY_2, -1);
1276             if (pDudeExtra->at4 && pXSprite->data3 > pDudeInfo->startHealth/3)
1277                 aiNewState(pSprite, pXSprite, &cerberusTeslaRecoil);
1278             else
1279                 aiNewState(pSprite, pXSprite, &cerberusRecoil);
1280             break;
1281         case kDudeCerberusOneHead:
1282             aiPlay3DSound(pSprite, 2302+Random(2), AI_SFX_PRIORITY_2, -1);
1283             aiNewState(pSprite, pXSprite, &cerberus2Recoil);
1284             break;
1285         case kDudeHellHound:
1286             aiPlay3DSound(pSprite, 1302, AI_SFX_PRIORITY_2, -1);
1287             if (pDudeExtra->at4)
1288                 aiNewState(pSprite, pXSprite, &houndTeslaRecoil);
1289             else
1290                 aiNewState(pSprite, pXSprite, &houndRecoil);
1291             break;
1292         case kDudeTchernobog:
1293             aiPlay3DSound(pSprite, 2370+Random(2), AI_SFX_PRIORITY_2, -1);
1294             aiNewState(pSprite, pXSprite, &tchernobogRecoil);
1295             break;
1296         case kDudeHand:
1297             aiPlay3DSound(pSprite, 1902, AI_SFX_PRIORITY_2, -1);
1298             aiNewState(pSprite, pXSprite, &handRecoil);
1299             break;
1300         case kDudeRat:
1301             aiPlay3DSound(pSprite, 2102, AI_SFX_PRIORITY_2, -1);
1302             aiNewState(pSprite, pXSprite, &ratRecoil);
1303             break;
1304         case kDudeBat:
1305             aiPlay3DSound(pSprite, 2002, AI_SFX_PRIORITY_2, -1);
1306             aiNewState(pSprite, pXSprite, &batRecoil);
1307             break;
1308         case kDudeBoneEel:
1309             aiPlay3DSound(pSprite, 1502, AI_SFX_PRIORITY_2, -1);
1310             aiNewState(pSprite, pXSprite, &eelRecoil);
1311             break;
1312         case kDudeGillBeast: {
1313             XSECTOR *pXSector = NULL;
1314             if (sector[pSprite->sectnum].extra > 0)
1315                 pXSector = &xsector[sector[pSprite->sectnum].extra];
1316             aiPlay3DSound(pSprite, 1702, AI_SFX_PRIORITY_2, -1);
1317             if (pXSector && pXSector->Underwater)
1318                 aiNewState(pSprite, pXSprite, &gillBeastSwimRecoil);
1319             else
1320                 aiNewState(pSprite, pXSprite, &gillBeastRecoil);
1321             break;
1322         }
1323         case kDudePhantasm:
1324             aiPlay3DSound(pSprite, 1602, AI_SFX_PRIORITY_2, -1);
1325             if (pDudeExtra->at4)
1326                 aiNewState(pSprite, pXSprite, &ghostTeslaRecoil);
1327             else
1328                 aiNewState(pSprite, pXSprite, &ghostRecoil);
1329             break;
1330         case kDudeSpiderBrown:
1331         case kDudeSpiderRed:
1332         case kDudeSpiderBlack:
1333             aiPlay3DSound(pSprite, 1802+Random(1), AI_SFX_PRIORITY_2, -1);
1334             aiNewState(pSprite, pXSprite, &spidDodge);
1335             break;
1336         case kDudeSpiderMother:
1337             aiPlay3DSound(pSprite, 1851+Random(1), AI_SFX_PRIORITY_2, -1);
1338             aiNewState(pSprite, pXSprite, &spidDodge);
1339             break;
1340         case kDudeInnocent:
1341             aiPlay3DSound(pSprite, 7007+Random(2), AI_SFX_PRIORITY_2, -1);
1342             if (pDudeExtra->at4)
1343                 aiNewState(pSprite, pXSprite, &innocentTeslaRecoil);
1344             else
1345                 aiNewState(pSprite, pXSprite, &innocentRecoil);
1346             break;
1347         case kDudeTinyCaleb:
1348             if (pXSprite->medium == kMediumNormal)
1349             {
1350                 if (pDudeExtra->at4)
1351                     aiNewState(pSprite, pXSprite, &tinycalebTeslaRecoil);
1352                 else
1353                     aiNewState(pSprite, pXSprite, &tinycalebRecoil);
1354             }
1355             else if (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo)
1356                 aiNewState(pSprite, pXSprite, &tinycalebSwimRecoil);
1357             else
1358             {
1359                 if (pDudeExtra->at4)
1360                     aiNewState(pSprite, pXSprite, &tinycalebTeslaRecoil);
1361                 else
1362                     aiNewState(pSprite, pXSprite, &tinycalebRecoil);
1363             }
1364             break;
1365         case kDudeBeast:
1366             aiPlay3DSound(pSprite, 9004+Random(2), AI_SFX_PRIORITY_2, -1);
1367             if (pXSprite->medium == kMediumNormal)
1368             {
1369                 if (pDudeExtra->at4)
1370                     aiNewState(pSprite, pXSprite, &beastTeslaRecoil);
1371                 else
1372                     aiNewState(pSprite, pXSprite, &beastRecoil);
1373             }
1374             else if (pXSprite->medium == kMediumWater || pXSprite->medium == kMediumGoo)
1375                 aiNewState(pSprite, pXSprite, &beastSwimRecoil);
1376             else
1377             {
1378                 if (pDudeExtra->at4)
1379                     aiNewState(pSprite, pXSprite, &beastTeslaRecoil);
1380                 else
1381                     aiNewState(pSprite, pXSprite, &beastRecoil);
1382             }
1383             break;
1384         case kDudePodGreen:
1385         case kDudePodFire:
1386             aiNewState(pSprite, pXSprite, &podRecoil);
1387             break;
1388         case kDudeTentacleGreen:
1389         case kDudeTentacleFire:
1390             aiNewState(pSprite, pXSprite, &tentacleRecoil);
1391             break;
1392         default:
1393             aiNewState(pSprite, pXSprite, &genRecoil);
1394             break;
1395         }
1396         pDudeExtra->at4 = 0;
1397     }
1398 }
1399 
aiThinkTarget(spritetype * pSprite,XSPRITE * pXSprite)1400 void aiThinkTarget(spritetype *pSprite, XSPRITE *pXSprite)
1401 {
1402     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
1403     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
1404     if (Chance(pDudeInfo->alertChance))
1405     {
1406         for (int p = connecthead; p >= 0; p = connectpoint2[p])
1407         {
1408             PLAYER *pPlayer = &gPlayer[p];
1409             if (pSprite->owner == pPlayer->nSprite || pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0)
1410                 continue;
1411             int x = pPlayer->pSprite->x;
1412             int y = pPlayer->pSprite->y;
1413             int z = pPlayer->pSprite->z;
1414             int nSector = pPlayer->pSprite->sectnum;
1415             int dx = x-pSprite->x;
1416             int dy = y-pSprite->y;
1417             int nDist = approxDist(dx, dy);
1418             if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist)
1419                 continue;
1420             if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum))
1421                 continue;
1422             int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
1423             if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
1424             {
1425                 aiSetTarget(pXSprite, pPlayer->nSprite);
1426                 aiActivateDude(pSprite, pXSprite);
1427                 return;
1428             }
1429             else if (nDist < pDudeInfo->hearDist)
1430             {
1431                 aiSetTarget(pXSprite, x, y, z);
1432                 aiActivateDude(pSprite, pXSprite);
1433                 return;
1434             }
1435         }
1436     }
1437 }
1438 
sub_5F15C(spritetype * pSprite,XSPRITE * pXSprite)1439 void sub_5F15C(spritetype *pSprite, XSPRITE *pXSprite)
1440 {
1441     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
1442     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
1443     if (Chance(pDudeInfo->alertChance))
1444     {
1445         for (int p = connecthead; p >= 0; p = connectpoint2[p])
1446         {
1447             PLAYER *pPlayer = &gPlayer[p];
1448             if (pSprite->owner == pPlayer->nSprite || pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0)
1449                 continue;
1450             int x = pPlayer->pSprite->x;
1451             int y = pPlayer->pSprite->y;
1452             int z = pPlayer->pSprite->z;
1453             int nSector = pPlayer->pSprite->sectnum;
1454             int dx = x-pSprite->x;
1455             int dy = y-pSprite->y;
1456             int nDist = approxDist(dx, dy);
1457             if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist)
1458                 continue;
1459             if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum))
1460                 continue;
1461             int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
1462             if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
1463             {
1464                 aiSetTarget(pXSprite, pPlayer->nSprite);
1465                 aiActivateDude(pSprite, pXSprite);
1466                 return;
1467             }
1468             else if (nDist < pDudeInfo->hearDist)
1469             {
1470                 aiSetTarget(pXSprite, x, y, z);
1471                 aiActivateDude(pSprite, pXSprite);
1472                 return;
1473             }
1474         }
1475         if (pXSprite->state)
1476         {
1477             char va4[(kMaxSectors+7)>>3];
1478             gAffectedSectors[0] = 0;
1479             gAffectedXWalls[0] = 0;
1480             GetClosestSpriteSectors(pSprite->sectnum, pSprite->x, pSprite->y, 400, gAffectedSectors, va4, gAffectedXWalls);
1481             for (int nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
1482             {
1483                 spritetype *pSprite2 = &sprite[nSprite2];
1484                 int dx = pSprite2->x-pSprite->x;
1485                 int dy = pSprite2->y-pSprite->y;
1486                 int nDist = approxDist(dx, dy);
1487                 if (pSprite2->type == kDudeInnocent)
1488                 {
1489                     DUDEINFO *pDudeInfo = getDudeInfo(pSprite2->type);
1490                     if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist)
1491                         continue;
1492                     int UNUSED(nAngle) = getangle(dx,dy);
1493                     aiSetTarget(pXSprite, pSprite2->index);
1494                     aiActivateDude(pSprite, pXSprite);
1495                     return;
1496                 }
1497             }
1498         }
1499     }
1500 }
1501 
aiProcessDudes(void)1502 void aiProcessDudes(void) {
1503     for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
1504         spritetype *pSprite = &sprite[nSprite];
1505         if (pSprite->flags & 32) continue;
1506         int nXSprite = pSprite->extra;
1507         XSPRITE *pXSprite = &xsprite[nXSprite]; DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
1508         if (IsPlayerSprite(pSprite) || pXSprite->health == 0) continue;
1509         pXSprite->stateTimer = ClipLow(pXSprite->stateTimer-4, 0);
1510 
1511         if (pXSprite->aiState->moveFunc)
1512             pXSprite->aiState->moveFunc(pSprite, pXSprite);
1513 
1514         if (pXSprite->aiState->thinkFunc && (gFrame & 3) == (nSprite & 3))
1515             pXSprite->aiState->thinkFunc(pSprite, pXSprite);
1516 
1517         switch (pSprite->type) {
1518             #ifdef NOONE_EXTENSIONS
1519             case kDudeModernCustom:
1520             case kDudeModernCustomBurning: {
1521                 GENDUDEEXTRA* pExtra = &gGenDudeExtra[pSprite->index];
1522                 if (pExtra->slaveCount > 0) updateTargetOfSlaves(pSprite);
1523                 if (pExtra->nLifeLeech >= 0) updateTargetOfLeech(pSprite);
1524                 if (pXSprite->stateTimer == 0 && pXSprite->aiState->nextState
1525                     && (pXSprite->aiState->stateTicks > 0 || seqGetStatus(3, pSprite->extra) < 0)) {
1526                     aiGenDudeNewState(pSprite, pXSprite->aiState->nextState);
1527                 }
1528                 int hinder = ((pExtra->isMelee) ? 25 : 5) << 4;
1529                 if (pXSprite->health <= 0 || hinder > cumulDamage[pSprite->extra]) break;
1530                 pXSprite->data3 = cumulDamage[pSprite->extra];
1531                 RecoilDude(pSprite, pXSprite);
1532                 break;
1533             }
1534             #endif
1535             default:
1536                 if (pXSprite->stateTimer == 0 && pXSprite->aiState->nextState) {
1537                     if (pXSprite->aiState->stateTicks > 0)
1538                         aiNewState(pSprite, pXSprite, pXSprite->aiState->nextState);
1539                     else if (seqGetStatus(3, nXSprite) < 0)
1540                         aiNewState(pSprite, pXSprite, pXSprite->aiState->nextState);
1541                 }
1542 
1543                 if (pXSprite->health > 0 && ((pDudeInfo->hinderDamage << 4) <= cumulDamage[nXSprite])) {
1544                     pXSprite->data3 = cumulDamage[nXSprite];
1545                     RecoilDude(pSprite, pXSprite);
1546                 }
1547                 break;
1548         }
1549     }
1550     memset(cumulDamage, 0, sizeof(cumulDamage));
1551 }
1552 
aiInit(void)1553 void aiInit(void) {
1554     for (int nSprite = headspritestat[kStatDude]; nSprite >= 0; nSprite = nextspritestat[nSprite]) {
1555         aiInitSprite(&sprite[nSprite]);
1556     }
1557 }
1558 
aiInitSprite(spritetype * pSprite)1559 void aiInitSprite(spritetype *pSprite)
1560 {
1561     int nXSprite = pSprite->extra;
1562     XSPRITE *pXSprite = &xsprite[nXSprite];
1563     int nSector = pSprite->sectnum;
1564     int nXSector = sector[nSector].extra;
1565     XSECTOR *pXSector = NULL;
1566     if (nXSector > 0)
1567         pXSector = &xsector[nXSector];
1568     DUDEEXTRA *pDudeExtra = &gDudeExtra[pSprite->extra];
1569     pDudeExtra->at4 = 0;
1570     pDudeExtra->at0 = 0;
1571 
1572     #ifdef NOONE_EXTENSIONS
1573     int stateTimer = -1, targetMarker = -1;
1574     int targetX = 0, targetY = 0, targetZ = 0;
1575 
1576     // dude patrol init
1577     if (gModernMap) {
1578 
1579         // must keep it in case of loading save
1580         if (pXSprite->dudeFlag4 && spriRangeIsFine(pXSprite->target) && sprite[pXSprite->target].type == kMarkerPath) {
1581             stateTimer = pXSprite->stateTimer; targetMarker = pXSprite->target;
1582             targetX = pXSprite->targetX; targetY = pXSprite->targetY;
1583             targetZ = pXSprite->targetZ;
1584         }
1585 
1586     }
1587     #endif
1588 
1589     switch (pSprite->type) {
1590     case kDudeCultistTommy:
1591     case kDudeCultistShotgun:
1592     case kDudeCultistTesla:
1593     case kDudeCultistTNT:
1594     case kDudeCultistBeast:
1595     {
1596         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1597         pDudeExtraE->at8 = 0;
1598         pDudeExtraE->at0 = 0;
1599         aiNewState(pSprite, pXSprite, &cultistIdle);
1600         break;
1601     }
1602     case kDudeCultistTommyProne:
1603     {
1604         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1605         pDudeExtraE->at8 = 0;
1606         pDudeExtraE->at0 = 0;
1607         aiNewState(pSprite, pXSprite, &fanaticProneIdle);
1608         break;
1609     }
1610     case kDudeCultistShotgunProne:
1611     {
1612         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1613         pDudeExtraE->at8 = 0;
1614         pDudeExtraE->at0 = 0;
1615         aiNewState(pSprite, pXSprite, &cultistProneIdle);
1616         break;
1617     }
1618     case kDudeZombieButcher: {
1619         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2;
1620         pDudeExtraE->at4 = 0;
1621         pDudeExtraE->at0 = 0;
1622         aiNewState(pSprite, pXSprite, &zombieFIdle);
1623         break;
1624     }
1625     case kDudeZombieAxeNormal: {
1626         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2;
1627         pDudeExtraE->at4 = 0;
1628         pDudeExtraE->at0 = 0;
1629         aiNewState(pSprite, pXSprite, &zombieAIdle);
1630         break;
1631     }
1632     case kDudeZombieAxeLaying:
1633     {
1634         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2;
1635         pDudeExtraE->at4 = 0;
1636         pDudeExtraE->at0 = 0;
1637         aiNewState(pSprite, pXSprite, &zombieSIdle);
1638         pSprite->flags &= ~1;
1639         break;
1640     }
1641     case kDudeZombieAxeBuried: {
1642         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2;
1643         pDudeExtraE->at4 = 0;
1644         pDudeExtraE->at0 = 0;
1645         aiNewState(pSprite, pXSprite, &zombieEIdle);
1646         break;
1647     }
1648     case kDudeGargoyleFlesh:
1649     case kDudeGargoyleStone: {
1650         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1651         pDudeExtraE->at4 = 0;
1652         pDudeExtraE->at8 = 0;
1653         pDudeExtraE->at0 = 0;
1654         aiNewState(pSprite, pXSprite, &gargoyleFIdle);
1655         break;
1656     }
1657     case kDudeGargoyleStatueFlesh:
1658     case kDudeGargoyleStatueStone:
1659         aiNewState(pSprite, pXSprite, &gargoyleStatueIdle);
1660         break;
1661     case kDudeCerberusTwoHead: {
1662         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2;
1663         pDudeExtraE->at4 = 0;
1664         pDudeExtraE->at0 = 0;
1665         aiNewState(pSprite, pXSprite, &cerberusIdle);
1666         break;
1667     }
1668     case kDudeHellHound:
1669         aiNewState(pSprite, pXSprite, &houndIdle);
1670         break;
1671     case kDudeHand:
1672         aiNewState(pSprite, pXSprite, &handIdle);
1673         break;
1674     case kDudePhantasm:
1675     {
1676         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1677         pDudeExtraE->at4 = 0;
1678         pDudeExtraE->at8 = 0;
1679         pDudeExtraE->at0 = 0;
1680         aiNewState(pSprite, pXSprite, &ghostIdle);
1681         break;
1682     }
1683     case kDudeInnocent:
1684         aiNewState(pSprite, pXSprite, &innocentIdle);
1685         break;
1686     case kDudeRat:
1687         aiNewState(pSprite, pXSprite, &ratIdle);
1688         break;
1689     case kDudeBoneEel:
1690     {
1691         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1692         pDudeExtraE->at4 = 0;
1693         pDudeExtraE->at8 = 0;
1694         pDudeExtraE->at0 = 0;
1695         aiNewState(pSprite, pXSprite, &eelIdle);
1696         break;
1697     }
1698     case kDudeGillBeast:
1699         aiNewState(pSprite, pXSprite, &gillBeastIdle);
1700         break;
1701     case kDudeBat:
1702     {
1703         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1704         pDudeExtraE->at4 = 0;
1705         pDudeExtraE->at8 = 0;
1706         pDudeExtraE->at0 = 0;
1707         aiNewState(pSprite, pXSprite, &batIdle);
1708         break;
1709     }
1710     case kDudeSpiderBrown:
1711     case kDudeSpiderRed:
1712     case kDudeSpiderBlack:
1713     {
1714         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1715         pDudeExtraE->at8 = 0;
1716         pDudeExtraE->at4 = 0;
1717         pDudeExtraE->at0 = 0;
1718         aiNewState(pSprite, pXSprite, &spidIdle);
1719         break;
1720     }
1721     case kDudeSpiderMother:
1722     {
1723         DUDEEXTRA_at6_u1 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u1;
1724         pDudeExtraE->at8 = 0;
1725         pDudeExtraE->at4 = 0;
1726         pDudeExtraE->at0 = 0;
1727         aiNewState(pSprite, pXSprite, &spidIdle);
1728         break;
1729     }
1730     case kDudeTchernobog:
1731     {
1732         DUDEEXTRA_at6_u2 *pDudeExtraE = &gDudeExtra[nXSprite].at6.u2;
1733         pDudeExtraE->at4 = 0;
1734         pDudeExtraE->at0 = 0;
1735         aiNewState(pSprite, pXSprite, &tchernobogIdle);
1736         break;
1737     }
1738     case kDudeTinyCaleb:
1739         aiNewState(pSprite, pXSprite, &tinycalebIdle);
1740         break;
1741     case kDudeBeast:
1742         aiNewState(pSprite, pXSprite, &beastIdle);
1743         break;
1744     case kDudePodGreen:
1745     case kDudePodFire:
1746         aiNewState(pSprite, pXSprite, &podIdle);
1747         break;
1748     case kDudeTentacleGreen:
1749     case kDudeTentacleFire:
1750         aiNewState(pSprite, pXSprite, &tentacleIdle);
1751         break;
1752     default:
1753         aiNewState(pSprite, pXSprite, &genIdle);
1754         break;
1755     }
1756     aiSetTarget(pXSprite, 0, 0, 0);
1757     pXSprite->stateTimer = 0;
1758     switch (pSprite->type)
1759     {
1760     case kDudeSpiderBrown:
1761     case kDudeSpiderRed:
1762     case kDudeSpiderBlack:
1763         if (pSprite->cstat&8) pSprite->flags |= 9;
1764         else pSprite->flags = 15;
1765         break;
1766     case kDudeGargoyleFlesh:
1767     case kDudeGargoyleStone:
1768     case kDudePhantasm:
1769     case kDudeBoneEel:
1770     case kDudeBat:
1771         pSprite->flags |= 9;
1772         break;
1773     case kDudeGillBeast:
1774         if (pXSector && pXSector->Underwater) pSprite->flags |= 9;
1775         else pSprite->flags = 15;
1776         break;
1777     case kDudeZombieAxeBuried:
1778     case kDudeZombieAxeLaying:
1779         pSprite->flags = 7;
1780         break;
1781     #ifdef NOONE_EXTENSIONS
1782     case kDudePodMother: // FakeDude type
1783         if (gModernMap) break;
1784         fallthrough__;
1785     // Allow put pods and tentacles on ceilings if sprite is y-flipped.
1786     case kDudePodGreen:
1787     case kDudeTentacleGreen:
1788     case kDudePodFire:
1789     case kDudeTentacleFire:
1790     case kDudeTentacleMother:
1791         if (gModernMap && (pSprite->cstat & CSTAT_SPRITE_YFLIP)) break;
1792         fallthrough__;
1793     // go default
1794     #endif
1795     default:
1796         pSprite->flags = 15;
1797         break;
1798     }
1799 
1800     #ifdef NOONE_EXTENSIONS
1801     if (gModernMap) {
1802 
1803         if (pSprite->type == kDudeModernCustom) {
1804             aiGenDudeInitSprite(pSprite, pXSprite);
1805             genDudePrepare(pSprite, kGenDudePropertyAll);
1806         }
1807 
1808         if (pXSprite->dudeFlag4) {
1809 
1810             // restore dude's path
1811             if (spriRangeIsFine(targetMarker)) {
1812                 pXSprite->target = targetMarker;
1813                 pXSprite->targetX = targetX;
1814                 pXSprite->targetY = targetY;
1815                 pXSprite->targetZ = targetZ;
1816             }
1817 
1818             // reset target spot progress
1819             pXSprite->data3 = 0;
1820 
1821             // make dude follow the markers
1822             bool uwater = spriteIsUnderwater(pSprite);
1823             if (stateTimer > 0) {
1824                 if (uwater) aiPatrolState(pSprite, kAiStatePatrolWaitW);
1825                 else if (pXSprite->unused1 & kDudeFlagCrouch) aiPatrolState(pSprite, kAiStatePatrolWaitC);
1826                 else aiPatrolState(pSprite, kAiStatePatrolWaitL);
1827                 pXSprite->stateTimer = stateTimer; // restore state timer
1828             }
1829             else if (uwater) aiPatrolState(pSprite, kAiStatePatrolMoveW);
1830             else if (pXSprite->unused1 & kDudeFlagCrouch) aiPatrolState(pSprite, kAiStatePatrolMoveC);
1831             else aiPatrolState(pSprite, kAiStatePatrolMoveL);
1832 
1833         }
1834 
1835     }
1836     #endif
1837 
1838 }
1839 
1840 class AILoadSave : public LoadSave
1841 {
1842     virtual void Load(void);
1843     virtual void Save(void);
1844 };
1845 
Load(void)1846 void AILoadSave::Load(void)
1847 {
1848     Read(cumulDamage, sizeof(cumulDamage));
1849     Read(gDudeSlope, sizeof(gDudeSlope));
1850 }
1851 
Save(void)1852 void AILoadSave::Save(void)
1853 {
1854     Write(cumulDamage, sizeof(cumulDamage));
1855     Write(gDudeSlope, sizeof(gDudeSlope));
1856 }
1857 
1858 static AILoadSave *myLoadSave;
1859 
AILoadSaveConstruct(void)1860 void AILoadSaveConstruct(void)
1861 {
1862     myLoadSave = new AILoadSave();
1863 }
1864 
1865