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