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