1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5 
6 This file is part of NBlood.
7 
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include "compat.h"
24 #include "build.h"
25 #include "pragmas.h"
26 #include "mmulti.h"
27 #include "common_game.h"
28 
29 #include "actor.h"
30 #include "ai.h"
31 #include "aibeast.h"
32 #include "blood.h"
33 #include "db.h"
34 #include "dude.h"
35 #include "levels.h"
36 #include "player.h"
37 #include "seq.h"
38 #include "sfx.h"
39 #include "trig.h"
40 
41 static void SlashSeqCallback(int, int);
42 static void StompSeqCallback(int, int);
43 static void MorphToBeast(spritetype *, XSPRITE *);
44 static void thinkSearch(spritetype *, XSPRITE *);
45 static void thinkGoto(spritetype *, XSPRITE *);
46 static void thinkChase(spritetype *, XSPRITE *);
47 static void thinkSwimGoto(spritetype *, XSPRITE *);
48 static void thinkSwimChase(spritetype *, XSPRITE *);
49 static void MoveForward(spritetype *, XSPRITE *);
50 static void sub_628A0(spritetype *, XSPRITE *);
51 static void sub_62AE0(spritetype *, XSPRITE *);
52 static void sub_62D7C(spritetype *, XSPRITE *);
53 
54 static int nSlashClient = seqRegisterClient(SlashSeqCallback);
55 static int nStompClient = seqRegisterClient(StompSeqCallback);
56 
57 AISTATE beastIdle = {kAiStateIdle, 0, -1, 0, NULL, NULL, aiThinkTarget, NULL };
58 AISTATE beastChase = {kAiStateChase, 8, -1, 0, NULL, MoveForward, thinkChase, NULL };
59 AISTATE beastDodge = { kAiStateMove, 8, -1, 60, NULL, aiMoveDodge, NULL, &beastChase };
60 AISTATE beastGoto = { kAiStateMove, 8, -1, 600, NULL, MoveForward, thinkGoto, &beastIdle };
61 AISTATE beastSlash = { kAiStateChase, 6, nSlashClient, 120, NULL, NULL, NULL, &beastChase };
62 AISTATE beastStomp = { kAiStateChase, 7, nStompClient, 120, NULL, NULL, NULL, &beastChase };
63 AISTATE beastSearch = { kAiStateSearch, 8, -1, 120, NULL, MoveForward, thinkSearch, &beastIdle };
64 AISTATE beastRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &beastDodge };
65 AISTATE beastTeslaRecoil = { kAiStateRecoil, 4, -1, 0, NULL, NULL, NULL, &beastDodge };
66 AISTATE beastSwimIdle = {kAiStateIdle, 9, -1, 0, NULL, NULL, aiThinkTarget, NULL };
67 AISTATE beastSwimChase = { kAiStateChase, 9, -1, 0, NULL, sub_628A0, thinkSwimChase, NULL };
68 AISTATE beastSwimDodge = { kAiStateMove, 9, -1, 90, NULL, aiMoveDodge, NULL, &beastSwimChase };
69 AISTATE beastSwimGoto = { kAiStateMove, 9, -1, 600, NULL, MoveForward, thinkSwimGoto, &beastSwimIdle };
70 AISTATE beastSwimSearch = { kAiStateSearch, 9, -1, 120, NULL, MoveForward, thinkSearch, &beastSwimIdle };
71 AISTATE beastSwimSlash = { kAiStateChase, 9, nSlashClient, 0, NULL, NULL, thinkSwimChase, &beastSwimChase };
72 AISTATE beastSwimRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &beastSwimDodge };
73 AISTATE beastMorphToBeast = { kAiStateOther, -1, -1, 0, MorphToBeast, NULL, NULL, &beastIdle };
74 AISTATE beastMorphFromCultist = { kAiStateOther, 2576, -1, 0, NULL, NULL, NULL, &beastMorphToBeast };
75 AISTATE beast138FB4 = { kAiStateOther, 9, -1, 120, NULL, sub_62AE0, thinkSwimChase, &beastSwimChase };
76 AISTATE beast138FD0 = { kAiStateOther, 9, -1, 0, NULL, sub_62D7C, thinkSwimChase, &beastSwimChase };
77 AISTATE beast138FEC = { kAiStateOther, 9, -1, 120, NULL, aiMoveTurn, NULL, &beastSwimChase };
78 
SlashSeqCallback(int,int nXSprite)79 static void SlashSeqCallback(int, int nXSprite)
80 {
81     XSPRITE *pXSprite = &xsprite[nXSprite];
82     int nSprite = pXSprite->reference;
83     spritetype *pSprite = &sprite[nSprite];
84     spritetype *pTarget = &sprite[pXSprite->target];
85     int dx = Cos(pSprite->ang)>>16;
86     int dy = Sin(pSprite->ang)>>16;
87     // Correct ?
88     int dz = pSprite->z-pTarget->z;
89     dx += Random3(4000-700*gGameOptions.nDifficulty);
90     dy += Random3(4000-700*gGameOptions.nDifficulty);
91     actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash);
92     actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash);
93     actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash);
94     sfxPlay3DSound(pSprite, 9012+Random(2), -1, 0);
95 }
96 
StompSeqCallback(int,int nXSprite)97 static void StompSeqCallback(int, int nXSprite)
98 {
99     char vb8[(kMaxSectors+7)>>3];
100     XSPRITE *pXSprite = &xsprite[nXSprite];
101     int nSprite = pXSprite->reference;
102     spritetype *pSprite = &sprite[nSprite];
103     int dx = Cos(pSprite->ang)>>16;
104     int dy = Sin(pSprite->ang)>>16;
105     int x = pSprite->x;
106     int y = pSprite->y;
107     int z = pSprite->z;
108     int vc = 400;
109     int nSector = pSprite->sectnum;
110     int v1c = 5+2*gGameOptions.nDifficulty;
111     int v10 = 25+30*gGameOptions.nDifficulty;
112     gAffectedSectors[0] = -1;
113     gAffectedXWalls[0] = -1;
114     GetClosestSpriteSectors(nSector, x, y, vc, gAffectedSectors, vb8, gAffectedXWalls);
115     char v4 = 0;
116     int v34 = -1;
117     int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
118     actHitcodeToData(hit, &gHitInfo, &v34, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
119     if (hit == 3 && v34 >= 0)
120     {
121         if (sprite[v34].statnum == kStatDude)
122             v4 = 0;
123     }
124     vc <<= 4;
125     for (int nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
126     {
127         if (nSprite != nSprite2 || v4)
128         {
129             spritetype *pSprite2 = &sprite[nSprite2];
130             if (pSprite2->extra > 0 && pSprite2->extra < kMaxXSprites)
131             {
132                 if (pSprite2->type == kDudeBeast)
133                     continue;
134                 if (pSprite2->flags&32)
135                     continue;
136                 if (TestBitString(vb8, pSprite2->sectnum) && CheckProximity(pSprite2, x, y, z, nSector, vc))
137                 {
138                     int top, bottom;
139                     GetSpriteExtents(pSprite, &top, &bottom);
140                     if (klabs(bottom-sector[nSector].floorz) == 0)
141                     {
142                         int dx = klabs(pSprite->x-pSprite2->x);
143                         int dy = klabs(pSprite->y-pSprite2->y);
144                         int nDist2 = ksqrt(dx*dx + dy*dy);
145                         if (nDist2 <= vc)
146                         {
147                             int nDamage;
148                             if (!nDist2)
149                                 nDamage = v1c + v10;
150                             else
151                                 nDamage = v1c + ((vc-nDist2)*v10)/vc;
152                             if (IsPlayerSprite(pSprite2))
153                                 gPlayer[pSprite2->type-kDudePlayer1].quakeEffect += nDamage*4;
154                             actDamageSprite(nSprite, pSprite2, kDamageFall, nDamage<<4);
155                         }
156                     }
157                 }
158             }
159         }
160     }
161     for (int nSprite2 = headspritestat[kStatThing]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2])
162     {
163         spritetype *pSprite2 = &sprite[nSprite2];
164         if (pSprite2->flags&32)
165             continue;
166         if (TestBitString(vb8, pSprite2->sectnum) && CheckProximity(pSprite2, x, y, z, nSector, vc))
167         {
168             XSPRITE *pXSprite = &xsprite[pSprite2->extra];
169             if (pXSprite->locked)
170                 continue;
171             int dx = klabs(pSprite->x-pSprite2->x);
172             int dy = klabs(pSprite->y-pSprite2->y);
173             int nDist2 = ksqrt(dx*dx + dy*dy);
174             if (nDist2 <= vc)
175             {
176                 int nDamage;
177                 if (!nDist2)
178                     nDamage = v1c + v10;
179                 else
180                     nDamage = v1c + ((vc-nDist2)*v10)/vc;
181                 if (IsPlayerSprite(pSprite2))
182                     gPlayer[pSprite2->type-kDudePlayer1].quakeEffect += nDamage*4;
183                 actDamageSprite(nSprite, pSprite2, kDamageFall, nDamage<<4);
184             }
185         }
186     }
187     sfxPlay3DSound(pSprite, 9015+Random(2), -1, 0);
188 }
189 
MorphToBeast(spritetype * pSprite,XSPRITE * pXSprite)190 static void MorphToBeast(spritetype *pSprite, XSPRITE *pXSprite)
191 {
192     actHealDude(pXSprite, dudeInfo[51].startHealth, dudeInfo[51].startHealth);
193     pSprite->type = kDudeBeast;
194 }
195 
thinkSearch(spritetype * pSprite,XSPRITE * pXSprite)196 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite)
197 {
198     aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng);
199     aiThinkTarget(pSprite, pXSprite);
200 }
201 
thinkGoto(spritetype * pSprite,XSPRITE * pXSprite)202 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite)
203 {
204     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
205     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
206     XSECTOR *pXSector;
207     int nXSector = sector[pSprite->sectnum].extra;
208     if (nXSector > 0)
209         pXSector = &xsector[nXSector];
210     else
211         pXSector = NULL;
212     int dx = pXSprite->targetX-pSprite->x;
213     int dy = pXSprite->targetY-pSprite->y;
214     int nAngle = getangle(dx, dy);
215     int nDist = approxDist(dx, dy);
216     aiChooseDirection(pSprite, pXSprite, nAngle);
217     if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
218     {
219         if (pXSector && pXSector->Underwater)
220             aiNewState(pSprite, pXSprite, &beastSwimSearch);
221         else
222             aiNewState(pSprite, pXSprite, &beastSearch);
223     }
224     aiThinkTarget(pSprite, pXSprite);
225 }
226 
thinkChase(spritetype * pSprite,XSPRITE * pXSprite)227 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite)
228 {
229     if (pXSprite->target == -1)
230     {
231         XSECTOR *pXSector;
232         int nXSector = sector[pSprite->sectnum].extra;
233         if (nXSector > 0)
234             pXSector = &xsector[nXSector];
235         else
236             pXSector = NULL;
237         if (pXSector && pXSector->Underwater)
238             aiNewState(pSprite, pXSprite, &beastSwimSearch);
239         else
240             aiNewState(pSprite, pXSprite, &beastSearch);
241         return;
242     }
243     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
244     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
245     dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
246     spritetype *pTarget = &sprite[pXSprite->target];
247     XSPRITE *pXTarget = &xsprite[pTarget->extra];
248     int dx = pTarget->x-pSprite->x;
249     int dy = pTarget->y-pSprite->y;
250     aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
251     if (pXTarget->health == 0)
252     {
253         XSECTOR *pXSector;
254         int nXSector = sector[pSprite->sectnum].extra;
255         if (nXSector > 0)
256             pXSector = &xsector[nXSector];
257         else
258             pXSector = NULL;
259         if (pXSector && pXSector->Underwater)
260             aiNewState(pSprite, pXSprite, &beastSwimSearch);
261         else
262             aiNewState(pSprite, pXSprite, &beastSearch);
263         return;
264     }
265     if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0)
266     {
267         XSECTOR *pXSector;
268         int nXSector = sector[pSprite->sectnum].extra;
269         if (nXSector > 0)
270             pXSector = &xsector[nXSector];
271         else
272             pXSector = NULL;
273         if (pXSector && pXSector->Underwater)
274             aiNewState(pSprite, pXSprite, &beastSwimSearch);
275         else
276             aiNewState(pSprite, pXSprite, &beastSearch);
277         return;
278     }
279     int nDist = approxDist(dx, dy);
280     if (nDist <= pDudeInfo->seeDist)
281     {
282         int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
283         int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2;
284         if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
285         {
286             if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
287             {
288                 aiSetTarget(pXSprite, pXSprite->target);
289                 int nXSprite = sprite[pXSprite->reference].extra;
290                 gDudeSlope[nXSprite] = divscale(pTarget->z-pSprite->z, nDist, 10);
291                 if (nDist < 0x1400 && nDist > 0xa00 && klabs(nDeltaAngle) < 85 && (pTarget->flags&2)
292                     && IsPlayerSprite(pTarget) && Chance(0x8000))
293                 {
294                     XSECTOR *pXSector;
295                     int nXSector = sector[pSprite->sectnum].extra;
296                     if (nXSector > 0)
297                         pXSector = &xsector[nXSector];
298                     else
299                         pXSector = NULL;
300                     int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
301                     if (pXTarget->health > gPlayerTemplate[0].startHealth/2)
302                     {
303                         switch (hit)
304                         {
305                         case -1:
306                             if (!pXSector || !pXSector->Underwater)
307                                 aiNewState(pSprite, pXSprite, &beastStomp);
308                             break;
309                         case 3:
310                             if (pSprite->type != sprite[gHitInfo.hitsprite].type)
311                             {
312                                 if (!pXSector || !pXSector->Underwater)
313                                     aiNewState(pSprite, pXSprite, &beastStomp);
314                             }
315                             else
316                             {
317                                 if (pXSector && pXSector->Underwater)
318                                     aiNewState(pSprite, pXSprite, &beastSwimDodge);
319                                 else
320                                     aiNewState(pSprite, pXSprite, &beastDodge);
321                             }
322                             break;
323                         default:
324                             if (!pXSector || !pXSector->Underwater)
325                                 aiNewState(pSprite, pXSprite, &beastStomp);
326                             break;
327                         }
328                     }
329                 }
330                 if (nDist < 921 && klabs(nDeltaAngle) < 28)
331                 {
332                     XSECTOR *pXSector;
333                     int nXSector = sector[pSprite->sectnum].extra;
334                     if (nXSector > 0)
335                         pXSector = &xsector[nXSector];
336                     else
337                         pXSector = NULL;
338                     int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0);
339                     switch (hit)
340                     {
341                     case -1:
342                         if (pXSector && pXSector->Underwater)
343                             aiNewState(pSprite, pXSprite, &beastSwimSlash);
344                         else
345                             aiNewState(pSprite, pXSprite, &beastSlash);
346                         break;
347                     case 3:
348                         if (pSprite->type != sprite[gHitInfo.hitsprite].type)
349                         {
350                             if (pXSector && pXSector->Underwater)
351                                 aiNewState(pSprite, pXSprite, &beastSwimSlash);
352                             else
353                                 aiNewState(pSprite, pXSprite, &beastSlash);
354                         }
355                         else
356                         {
357                             if (pXSector && pXSector->Underwater)
358                                 aiNewState(pSprite, pXSprite, &beastSwimDodge);
359                             else
360                                 aiNewState(pSprite, pXSprite, &beastDodge);
361                         }
362                         break;
363                     default:
364                         if (pXSector && pXSector->Underwater)
365                             aiNewState(pSprite, pXSprite, &beastSwimSlash);
366                         else
367                             aiNewState(pSprite, pXSprite, &beastSlash);
368                         break;
369                     }
370                 }
371             }
372             return;
373         }
374     }
375 
376     XSECTOR *pXSector;
377     int nXSector = sector[pSprite->sectnum].extra;
378     if (nXSector > 0)
379         pXSector = &xsector[nXSector];
380     else
381         pXSector = NULL;
382     if (pXSector && pXSector->Underwater)
383         aiNewState(pSprite, pXSprite, &beastSwimGoto);
384     else
385         aiNewState(pSprite, pXSprite, &beastGoto);
386     pXSprite->target = -1;
387 }
388 
thinkSwimGoto(spritetype * pSprite,XSPRITE * pXSprite)389 static void thinkSwimGoto(spritetype *pSprite, XSPRITE *pXSprite)
390 {
391     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
392     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
393     int dx = pXSprite->targetX-pSprite->x;
394     int dy = pXSprite->targetY-pSprite->y;
395     int nAngle = getangle(dx, dy);
396     int nDist = approxDist(dx, dy);
397     aiChooseDirection(pSprite, pXSprite, nAngle);
398     if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery)
399         aiNewState(pSprite, pXSprite, &beastSwimSearch);
400     aiThinkTarget(pSprite, pXSprite);
401 }
402 
thinkSwimChase(spritetype * pSprite,XSPRITE * pXSprite)403 static void thinkSwimChase(spritetype *pSprite, XSPRITE *pXSprite)
404 {
405     if (pXSprite->target == -1)
406     {
407         aiNewState(pSprite, pXSprite, &beastSwimGoto);
408         return;
409     }
410     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
411     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
412     dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites);
413     spritetype *pTarget = &sprite[pXSprite->target];
414     XSPRITE *pXTarget = &xsprite[pTarget->extra];
415     int dx = pTarget->x-pSprite->x;
416     int dy = pTarget->y-pSprite->y;
417     aiChooseDirection(pSprite, pXSprite, getangle(dx, dy));
418     if (pXTarget->health == 0)
419     {
420         aiNewState(pSprite, pXSprite, &beastSwimSearch);
421         return;
422     }
423     if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0)
424     {
425         aiNewState(pSprite, pXSprite, &beastSwimSearch);
426         return;
427     }
428     int nDist = approxDist(dx, dy);
429     if (nDist <= pDudeInfo->seeDist)
430     {
431         int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024;
432         int height = pDudeInfo->eyeHeight+pSprite->z;
433         int top, bottom;
434         GetSpriteExtents(pSprite, &top, &bottom);
435         if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum))
436         {
437             if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery)
438             {
439                 aiSetTarget(pXSprite, pXSprite->target);
440                 int UNUSED(floorZ) = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y);
441                 if (nDist < 0x400 && klabs(nDeltaAngle) < 85)
442                     aiNewState(pSprite, pXSprite, &beastSwimSlash);
443                 else
444                 {
445                     aiPlay3DSound(pSprite, 9009+Random(2), AI_SFX_PRIORITY_1, -1);
446                     aiNewState(pSprite, pXSprite, &beast138FD0);
447                 }
448             }
449         }
450         else
451             aiNewState(pSprite, pXSprite, &beast138FD0);
452         return;
453     }
454     aiNewState(pSprite, pXSprite, &beastSwimGoto);
455     pXSprite->target = -1;
456 }
457 
MoveForward(spritetype * pSprite,XSPRITE * pXSprite)458 static void MoveForward(spritetype *pSprite, XSPRITE *pXSprite)
459 {
460     int nSprite = pSprite->index;
461     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
462     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
463     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
464     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
465     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
466     if (klabs(nAng) > 341)
467         return;
468     int dx = pXSprite->targetX-pSprite->x;
469     int dy = pXSprite->targetY-pSprite->y;
470     int UNUSED(nAngle) = getangle(dx, dy);
471     int nDist = approxDist(dx, dy);
472     if (nDist <= 0x400 && Random(64) < 32)
473         return;
474     xvel[nSprite] += mulscale30(pDudeInfo->frontSpeed, Cos(pSprite->ang));
475     yvel[nSprite] += mulscale30(pDudeInfo->frontSpeed, Sin(pSprite->ang));
476 }
477 
sub_628A0(spritetype * pSprite,XSPRITE * pXSprite)478 static void sub_628A0(spritetype *pSprite, XSPRITE *pXSprite)
479 {
480     int nSprite = pSprite->index;
481     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
482     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
483     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
484     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
485     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
486     int nAccel = pDudeInfo->frontSpeed<<2;
487     if (klabs(nAng) > 341)
488         return;
489     if (pXSprite->target == -1)
490         pSprite->ang = (pSprite->ang+256)&2047;
491     int dx = pXSprite->targetX-pSprite->x;
492     int dy = pXSprite->targetY-pSprite->y;
493     int UNUSED(nAngle) = getangle(dx, dy);
494     int nDist = approxDist(dx, dy);
495     if (Random(64) < 32 && nDist <= 0x400)
496         return;
497     int nCos = Cos(pSprite->ang);
498     int nSin = Sin(pSprite->ang);
499     int vx = xvel[nSprite];
500     int vy = yvel[nSprite];
501     int t1 = dmulscale30(vx, nCos, vy, nSin);
502     int t2 = dmulscale30(vx, nSin, -vy, nCos);
503     if (pXSprite->target == -1)
504         t1 += nAccel;
505     else
506         t1 += nAccel>>2;
507     xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
508     yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
509 }
510 
sub_62AE0(spritetype * pSprite,XSPRITE * pXSprite)511 static void sub_62AE0(spritetype *pSprite, XSPRITE *pXSprite)
512 {
513     int nSprite = pSprite->index;
514     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
515     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
516     spritetype *pTarget = &sprite[pXSprite->target];
517     int z = pSprite->z + getDudeInfo(pSprite->type)->eyeHeight;
518     int z2 = pTarget->z + getDudeInfo(pTarget->type)->eyeHeight;
519     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
520     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
521     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
522     int nAccel = pDudeInfo->frontSpeed<<2;
523     if (klabs(nAng) > 341)
524     {
525         pXSprite->goalAng = (pSprite->ang+512)&2047;
526         return;
527     }
528     int dx = pXSprite->targetX-pSprite->x;
529     int dy = pXSprite->targetY-pSprite->y;
530     int dz = z2 - z;
531     int UNUSED(nAngle) = getangle(dx, dy);
532     int nDist = approxDist(dx, dy);
533     if (Chance(0x600) && nDist <= 0x400)
534         return;
535     int nCos = Cos(pSprite->ang);
536     int nSin = Sin(pSprite->ang);
537     int vx = xvel[nSprite];
538     int vy = yvel[nSprite];
539     int t1 = dmulscale30(vx, nCos, vy, nSin);
540     int t2 = dmulscale30(vx, nSin, -vy, nCos);
541     t1 += nAccel;
542     xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
543     yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
544     zvel[nSprite] = -dz;
545 }
546 
sub_62D7C(spritetype * pSprite,XSPRITE * pXSprite)547 static void sub_62D7C(spritetype *pSprite, XSPRITE *pXSprite)
548 {
549     int nSprite = pSprite->index;
550     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
551     DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
552     spritetype *pTarget = &sprite[pXSprite->target];
553     int z = pSprite->z + getDudeInfo(pSprite->type)->eyeHeight;
554     int z2 = pTarget->z + getDudeInfo(pTarget->type)->eyeHeight;
555     int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024;
556     int nTurnRange = (pDudeInfo->angSpeed<<2)>>4;
557     pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047;
558     int nAccel = pDudeInfo->frontSpeed<<2;
559     if (klabs(nAng) > 341)
560     {
561         pSprite->ang = (pSprite->ang+512)&2047;
562         return;
563     }
564     int dx = pXSprite->targetX-pSprite->x;
565     int dy = pXSprite->targetY-pSprite->y;
566     int dz = (z2 - z)<<3;
567     int UNUSED(nAngle) = getangle(dx, dy);
568     int nDist = approxDist(dx, dy);
569     if (Chance(0x4000) && nDist <= 0x400)
570         return;
571     int nCos = Cos(pSprite->ang);
572     int nSin = Sin(pSprite->ang);
573     int vx = xvel[nSprite];
574     int vy = yvel[nSprite];
575     int t1 = dmulscale30(vx, nCos, vy, nSin);
576     int t2 = dmulscale30(vx, nSin, -vy, nCos);
577     t1 += nAccel>>1;
578     xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin);
579     yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos);
580     zvel[nSprite] = dz;
581 }
582