aigarg.cpp
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 30 #include "actor.h" 31 #include "ai.h" 32 #include "aigarg.h" 33 #include "blood.h" 34 #include "db.h" 35 #include "dude.h" 36 #include "eventq.h" 37 #include "levels.h" 38 #include "player.h" 39 #include "seq.h" 40 #include "sfx.h" 41 #include "trig.h" 42 #ifdef NOONE_EXTENSIONS 43 #include "nnexts.h" 44 #endif 45 46 static void SlashFSeqCallback(int, int); 47 static void ThrowFSeqCallback(int, int); 48 static void BlastSSeqCallback(int, int); 49 static void ThrowSSeqCallback(int, int); 50 static void thinkTarget(spritetype *, XSPRITE *); 51 static void thinkSearch(spritetype *, XSPRITE *); 52 static void thinkGoto(spritetype *, XSPRITE *); 53 static void MoveDodgeUp(spritetype *, XSPRITE *); 54 static void MoveDodgeDown(spritetype *, XSPRITE *); 55 static void thinkChase(spritetype *, XSPRITE *); 56 static void entryFStatue(spritetype *, XSPRITE *); 57 static void entrySStatue(spritetype *, XSPRITE *); 58 static void MoveForward(spritetype *, XSPRITE *); 59 static void MoveSlow(spritetype *, XSPRITE *); 60 static void MoveSwoop(spritetype *, XSPRITE *); 61 static void MoveFly(spritetype *, XSPRITE *); 62 static void playStatueBreakSnd(spritetype*,XSPRITE*); 63 64 static int nSlashFClient = seqRegisterClient(SlashFSeqCallback); 65 static int nThrowFClient = seqRegisterClient(ThrowFSeqCallback); 66 static int nThrowSClient = seqRegisterClient(ThrowSSeqCallback); 67 static int nBlastSClient = seqRegisterClient(BlastSSeqCallback); 68 69 AISTATE gargoyleFIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, thinkTarget, NULL }; 70 AISTATE gargoyleStatueIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, NULL, NULL }; 71 AISTATE gargoyleFChase = { kAiStateChase, 0, -1, 0, NULL, MoveForward, thinkChase, &gargoyleFIdle }; 72 AISTATE gargoyleFGoto = { kAiStateMove, 0, -1, 600, NULL, MoveForward, thinkGoto, &gargoyleFIdle }; 73 AISTATE gargoyleFSlash = { kAiStateChase, 6, nSlashFClient, 120, NULL, NULL, NULL, &gargoyleFChase }; 74 AISTATE gargoyleFThrow = { kAiStateChase, 6, nThrowFClient, 120, NULL, NULL, NULL, &gargoyleFChase }; 75 AISTATE gargoyleSThrow = { kAiStateChase, 6, nThrowSClient, 120, NULL, MoveForward, NULL, &gargoyleFChase }; 76 AISTATE gargoyleSBlast = { kAiStateChase, 7, nBlastSClient, 60, NULL, MoveSlow, NULL, &gargoyleFChase }; 77 AISTATE gargoyleFRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &gargoyleFChase }; 78 AISTATE gargoyleFSearch = { kAiStateSearch, 0, -1, 120, NULL, MoveForward, thinkSearch, &gargoyleFIdle }; 79 AISTATE gargoyleFMorph2 = { kAiStateOther, -1, -1, 0, entryFStatue, NULL, NULL, &gargoyleFIdle }; 80 AISTATE gargoyleFMorph = { kAiStateOther, 6, -1, 0, NULL, NULL, NULL, &gargoyleFMorph2 }; 81 AISTATE gargoyleSMorph2 = { kAiStateOther, -1, -1, 0, entrySStatue, NULL, NULL, &gargoyleFIdle }; 82 AISTATE gargoyleSMorph = { kAiStateOther, 6, -1, 0, NULL, NULL, NULL, &gargoyleSMorph2 }; 83 AISTATE gargoyleSwoop = { kAiStateOther, 0, -1, 120, NULL, MoveSwoop, thinkChase, &gargoyleFChase }; 84 AISTATE gargoyleFly = { kAiStateMove, 0, -1, 120, NULL, MoveFly, thinkChase, &gargoyleFChase }; 85 AISTATE gargoyleTurn = { kAiStateMove, 0, -1, 120, NULL, aiMoveTurn, NULL, &gargoyleFChase }; 86 AISTATE gargoyleDodgeUp = { kAiStateMove, 0, -1, 60, NULL, MoveDodgeUp, NULL, &gargoyleFChase }; 87 AISTATE gargoyleFDodgeUpRight = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeUp, NULL, &gargoyleFChase }; 88 AISTATE gargoyleFDodgeUpLeft = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeUp, NULL, &gargoyleFChase }; 89 AISTATE gargoyleDodgeDown = { kAiStateMove, 0, -1, 120, NULL, MoveDodgeDown, NULL, &gargoyleFChase }; 90 AISTATE gargoyleFDodgeDownRight = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeDown, NULL, &gargoyleFChase }; 91 AISTATE gargoyleFDodgeDownLeft = { kAiStateMove, 0, -1, 90, NULL, MoveDodgeDown, NULL, &gargoyleFChase }; 92 93 AISTATE statueFBreakSEQ = { kAiStateOther, 5, -1, 0, entryFStatue, NULL, playStatueBreakSnd, &gargoyleFMorph2}; 94 AISTATE statueSBreakSEQ = { kAiStateOther, 5, -1, 0, entrySStatue, NULL, playStatueBreakSnd, &gargoyleSMorph2}; 95 96 static void playStatueBreakSnd(spritetype* pSprite, XSPRITE* pXSprite) { 97 UNREFERENCED_PARAMETER(pXSprite); 98 aiPlay3DSound(pSprite, 313, AI_SFX_PRIORITY_1, -1); 99 } 100 101 inline void SlashFSeqCallbackFixed(spritetype *pSprite, XSPRITE *pXSprite, spritetype *pTarget) 102 { 103 int tx = pXSprite->targetX-pSprite->x; 104 int ty = pXSprite->targetY-pSprite->y; 105 int nAngle = getangle(tx, ty); 106 int dx = Cos(nAngle)>>16; 107 int dy = Sin(nAngle)>>16; 108 int dz = pTarget->z-pSprite->z; 109 const int bakVecDist = gVectorData[kVectorGargSlash].maxDist; 110 if (pSprite->type == kDudeGargoyleStone) // only increase slash distance by 150% for Cheogh 111 gVectorData[kVectorGargSlash].maxDist += gVectorData[kVectorGargSlash].maxDist>>1; 112 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash); 113 int r1 = Random(50); 114 int r2 = Random(50); 115 actFireVector(pSprite, 0, 0, dx+r2, dy-r1, dz, kVectorGargSlash); 116 r1 = Random(50); 117 r2 = Random(50); 118 actFireVector(pSprite, 0, 0, dx-r2, dy+r1, dz, kVectorGargSlash); 119 if (pSprite->type == kDudeGargoyleStone) 120 gVectorData[kVectorGargSlash].maxDist = bakVecDist; 121 } 122 123 static void SlashFSeqCallback(int, int nXSprite) 124 { 125 XSPRITE *pXSprite = &xsprite[nXSprite]; 126 int nSprite = pXSprite->reference; 127 spritetype *pSprite = &sprite[nSprite]; 128 spritetype *pTarget = &sprite[pXSprite->target]; 129 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 130 DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type); 131 if (EnemiesNotBlood() && !VanillaMode()) // use fixed calculation and increase vector distance 132 return SlashFSeqCallbackFixed(pSprite, pXSprite, pTarget); 133 int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2; 134 int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2; 135 int dz = height-height2; 136 int dx = Cos(pSprite->ang)>>16; 137 int dy = Sin(pSprite->ang)>>16; 138 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorGargSlash); 139 int r1 = Random(50); 140 int r2 = Random(50); 141 actFireVector(pSprite, 0, 0, dx+r2, dy-r1, dz, kVectorGargSlash); 142 r1 = Random(50); 143 r2 = Random(50); 144 actFireVector(pSprite, 0, 0, dx-r2, dy+r1, dz, kVectorGargSlash); 145 } 146 147 static void ThrowFSeqCallback(int, int nXSprite) 148 { 149 XSPRITE *pXSprite = &xsprite[nXSprite]; 150 int nSprite = pXSprite->reference; 151 spritetype *pSprite = &sprite[nSprite]; 152 if (!sectRangeIsFine(pSprite->sectnum)) // invalid sector, abort 153 return; 154 actFireThing(pSprite, 0, 0, gDudeSlope[nXSprite]-7500, kThingBone, 0xeeeee); 155 } 156 157 static void BlastSSeqCallback(int, int nXSprite) 158 { 159 XSPRITE *pXSprite = &xsprite[nXSprite]; 160 int nSprite = pXSprite->reference; 161 spritetype *pSprite = &sprite[nSprite]; 162 wrand(); // ??? 163 spritetype *pTarget = &sprite[pXSprite->target]; 164 int height = (pSprite->yrepeat*getDudeInfo(pSprite->type)->eyeHeight) << 2; 165 int dx = pXSprite->targetX-pSprite->x; 166 int dy = pXSprite->targetY-pSprite->y; 167 int UNUSED(nDist) = approxDist(dx, dy); 168 int UNUSED(nAngle) = getangle(dx, dy); 169 int x = pSprite->x; 170 int y = pSprite->y; 171 int z = height; 172 TARGETTRACK tt = { 0x10000, 0x10000, 0x100, 0x55, 0x1aaaaa }; 173 Aim aim; 174 aim.dx = Cos(pSprite->ang)>>16; 175 aim.dy = Sin(pSprite->ang)>>16; 176 aim.dz = gDudeSlope[nXSprite]; 177 int nClosest = 0x7fffffff; 178 for (short nSprite2 = headspritestat[kStatDude]; nSprite2 >= 0; nSprite2 = nextspritestat[nSprite2]) 179 { 180 spritetype *pSprite2 = &sprite[nSprite2]; 181 if (pSprite == pSprite2 || !(pSprite2->flags&8)) 182 continue; 183 int x2 = pSprite2->x; 184 int y2 = pSprite2->y; 185 int z2 = pSprite2->z; 186 int nDist = approxDist(x2-x, y2-y); 187 if (nDist == 0 || nDist > 0x2800) 188 continue; 189 if (tt.at10) 190 { 191 int t = divscale12(nDist, tt.at10); 192 x2 += (xvel[nSprite2]*t)>>12; 193 y2 += (yvel[nSprite2]*t)>>12; 194 z2 += (zvel[nSprite2]*t)>>8; 195 } 196 int tx = x+mulscale30(Cos(pSprite->ang), nDist); 197 int ty = y+mulscale30(Sin(pSprite->ang), nDist); 198 int tz = z+mulscale10(gDudeSlope[nXSprite], nDist); 199 int tsr = mulscale10(9460, nDist); 200 int top, bottom; 201 GetSpriteExtents(pSprite2, &top, &bottom); 202 if (tz-tsr > bottom || tz+tsr < top) 203 { 204 if (IsDudeSprite(pSprite2) && EnemiesNotBlood() && !VanillaMode()) // use fixed calculation for missile projectile 205 aim.dz = divscale10(pSprite2->z-pSprite->z, ClipHigh(nDist, 0x1800)); 206 continue; 207 } 208 int dx = (tx-x2)>>4; 209 int dy = (ty-y2)>>4; 210 int dz = (tz-z2)>>8; 211 int nDist2 = ksqrt(dx*dx+dy*dy+dz*dz); 212 if (nDist2 < nClosest) 213 { 214 int nAngle = getangle(x2-x, y2-y); 215 int nDeltaAngle = ((nAngle-pSprite->ang+1024)&2047)-1024; 216 if (klabs(nDeltaAngle) <= tt.at8) 217 { 218 int tz = pSprite2->z-pSprite->z; 219 if (cansee(x, y, z, pSprite->sectnum, x2, y2, z2, pSprite2->sectnum)) 220 { 221 nClosest = nDist2; 222 aim.dx = Cos(nAngle)>>16; 223 aim.dy = Sin(nAngle)>>16; 224 aim.dz = divscale10(tz, nDist); 225 if (IsDudeSprite(pSprite2) && EnemiesNotBlood() && !VanillaMode()) // use fixed calculation for missile projectile 226 continue; 227 if (tz > -0x333) 228 aim.dz = divscale10(tz, nDist); 229 else if (tz < -0x333 && tz > -0xb33) 230 aim.dz = divscale10(tz, nDist)+9460; 231 else if (tz < -0xb33 && tz > -0x3000) 232 aim.dz = divscale10(tz, nDist)+9460; 233 else if (tz < -0x3000) 234 aim.dz = divscale10(tz, nDist)-7500; 235 else 236 aim.dz = divscale10(tz, nDist); 237 } 238 else 239 aim.dz = divscale10(tz, nDist); 240 } 241 } 242 } 243 #ifdef NOONE_EXTENSIONS 244 // allow to fire missile in non-player targets 245 if (IsPlayerSprite(pTarget) || gModernMap) { 246 actFireMissile(pSprite, -120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle); 247 actFireMissile(pSprite, 120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle); 248 } 249 #else 250 if (IsPlayerSprite(pTarget)) { 251 actFireMissile(pSprite, -120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle); 252 actFireMissile(pSprite, 120, 0, aim.dx, aim.dy, aim.dz, kMissileArcGargoyle); 253 } 254 #endif 255 256 } 257 258 static void ThrowSSeqCallback(int, int nXSprite) 259 { 260 XSPRITE *pXSprite = &xsprite[nXSprite]; 261 int nSprite = pXSprite->reference; 262 spritetype *pSprite = &sprite[nSprite]; 263 if (!sectRangeIsFine(pSprite->sectnum)) // invalid sector, abort 264 return; 265 actFireThing(pSprite, 0, 0, gDudeSlope[nXSprite]-7500, kThingBone, Chance(0x6000) ? 0x133333 : 0x111111); 266 } 267 268 static void thinkTarget(spritetype *pSprite, XSPRITE *pXSprite) 269 { 270 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 271 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 272 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 273 return; 274 } 275 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 276 DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats; 277 if (pDudeExtraE->active && pDudeExtraE->thinkTime < 10) 278 pDudeExtraE->thinkTime++; 279 else if (pDudeExtraE->thinkTime >= 10 && pDudeExtraE->active) 280 { 281 pXSprite->goalAng += 256; 282 POINT3D *pTarget = &baseSprite[pSprite->index]; 283 aiSetTarget(pXSprite, pTarget->x, pTarget->y, pTarget->z); 284 aiNewState(pSprite, pXSprite, &gargoyleTurn); 285 return; 286 } 287 if (Chance(pDudeInfo->alertChance)) 288 { 289 for (int p = connecthead; p >= 0; p = connectpoint2[p]) 290 { 291 PLAYER *pPlayer = &gPlayer[p]; 292 if (pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0) 293 continue; 294 int x = pPlayer->pSprite->x; 295 int y = pPlayer->pSprite->y; 296 int z = pPlayer->pSprite->z; 297 int nSector = pPlayer->pSprite->sectnum; 298 int dx = x-pSprite->x; 299 int dy = y-pSprite->y; 300 int nDist = approxDist(dx, dy); 301 if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist) 302 continue; 303 if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum)) 304 continue; 305 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 306 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 307 { 308 pDudeExtraE->thinkTime = 0; 309 aiSetTarget(pXSprite, pPlayer->nSprite); 310 aiActivateDude(pSprite, pXSprite); 311 } 312 else if (nDist < pDudeInfo->hearDist) 313 { 314 pDudeExtraE->thinkTime = 0; 315 aiSetTarget(pXSprite, x, y, z); 316 aiActivateDude(pSprite, pXSprite); 317 } 318 else 319 continue; 320 break; 321 } 322 } 323 } 324 325 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite) 326 { 327 aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); 328 aiLookForTarget(pSprite, pXSprite); 329 } 330 331 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite) 332 { 333 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 334 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 335 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 336 return; 337 } 338 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 339 int dx = pXSprite->targetX-pSprite->x; 340 int dy = pXSprite->targetY-pSprite->y; 341 int nAngle = getangle(dx, dy); 342 int nDist = approxDist(dx, dy); 343 aiChooseDirection(pSprite, pXSprite, nAngle); 344 if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery) 345 aiNewState(pSprite, pXSprite, &gargoyleFSearch); 346 aiThinkTarget(pSprite, pXSprite); 347 } 348 349 static void MoveDodgeUp(spritetype *pSprite, XSPRITE *pXSprite) 350 { 351 int nSprite = pSprite->index; 352 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 353 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 354 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 355 return; 356 } 357 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 358 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 359 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 360 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 361 int nCos = Cos(pSprite->ang); 362 int nSin = Sin(pSprite->ang); 363 int dx = xvel[nSprite]; 364 int dy = yvel[nSprite]; 365 int t1 = dmulscale30(dx, nCos, dy, nSin); 366 int t2 = dmulscale30(dx, nSin, -dy, nCos); 367 if (pXSprite->dodgeDir > 0) 368 t2 += pDudeInfo->sideSpeed; 369 else 370 t2 -= pDudeInfo->sideSpeed; 371 372 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 373 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 374 zvel[nSprite] = -0x1d555; 375 } 376 377 static void MoveDodgeDown(spritetype *pSprite, XSPRITE *pXSprite) 378 { 379 int nSprite = pSprite->index; 380 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 381 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 382 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 383 return; 384 } 385 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 386 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 387 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 388 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 389 if (pXSprite->dodgeDir == 0) 390 return; 391 int nCos = Cos(pSprite->ang); 392 int nSin = Sin(pSprite->ang); 393 int dx = xvel[nSprite]; 394 int dy = yvel[nSprite]; 395 int t1 = dmulscale30(dx, nCos, dy, nSin); 396 int t2 = dmulscale30(dx, nSin, -dy, nCos); 397 if (pXSprite->dodgeDir > 0) 398 t2 += pDudeInfo->sideSpeed; 399 else 400 t2 -= pDudeInfo->sideSpeed; 401 402 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 403 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 404 zvel[nSprite] = 0x44444; 405 } 406 407 inline int thinkChaseGetTargetHeight(spritetype *pSprite, DUDEINFO *pDudeInfo, spritetype *pTarget) 408 { 409 if (VanillaMode() || !EnemiesNotBlood()) 410 return 0; 411 DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type); 412 int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2; 413 int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2; 414 return height-height2; 415 } 416 417 inline void thinkAirBrakes(int nSprite) 418 { 419 if (VanillaMode() || !EnemiesNotBlood() || !spriRangeIsFine(nSprite)) 420 return; 421 xvel[nSprite] = -(xvel[nSprite]>>2); 422 yvel[nSprite] = -(yvel[nSprite]>>2); 423 } 424 425 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite) 426 { 427 if (pXSprite->target == -1) 428 { 429 aiNewState(pSprite, pXSprite, &gargoyleFGoto); 430 return; 431 } 432 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 433 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 434 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 435 return; 436 } 437 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 438 ///dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 439 if (!(pXSprite->target >= 0 && pXSprite->target < kMaxSprites)) { 440 consoleSysMsg("pXSprite->target >= 0 && pXSprite->target < kMaxSprites"); 441 return; 442 } 443 spritetype *pTarget = &sprite[pXSprite->target]; 444 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 445 int dx = pTarget->x-pSprite->x; 446 int dy = pTarget->y-pSprite->y; 447 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 448 if (pXTarget->health == 0) 449 { 450 aiNewState(pSprite, pXSprite, &gargoyleFSearch); 451 return; 452 } 453 if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0) 454 { 455 aiNewState(pSprite, pXSprite, &gargoyleFSearch); 456 return; 457 } 458 int nDist = approxDist(dx, dy); 459 if (nDist <= pDudeInfo->seeDist) 460 { 461 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 462 int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2; 463 // Should be dudeInfo[pTarget->type-kDudeBase] 464 int height2 = (pDudeInfo->eyeHeight*pTarget->yrepeat)<<2; 465 int top, bottom; 466 GetSpriteExtents(pSprite, &top, &bottom); 467 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 468 { 469 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 470 { 471 aiSetTarget(pXSprite, pXSprite->target); 472 int floorZ = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y); 473 switch (pSprite->type) { 474 case kDudeGargoyleFlesh: 475 if (nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85) 476 { 477 int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget); 478 int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0); 479 switch (hit) 480 { 481 case -1: 482 sfxPlay3DSound(pSprite, 1408, 0, 0); 483 aiNewState(pSprite, pXSprite, &gargoyleFThrow); 484 break; 485 case 0: 486 case 4: 487 break; 488 case 3: 489 if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleStone) 490 { 491 sfxPlay3DSound(pSprite, 1408, 0, 0); 492 aiNewState(pSprite, pXSprite, &gargoyleFThrow); 493 } 494 break; 495 default: 496 sfxPlay3DSound(pSprite, 1408, 0, 0); 497 aiNewState(pSprite, pXSprite, &gargoyleFThrow); 498 break; 499 } 500 } 501 else if (nDist < 0x400 && klabs(nDeltaAngle) < 85) 502 { 503 int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget); 504 int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0); 505 switch (hit) 506 { 507 case -1: 508 sfxPlay3DSound(pSprite, 1406, 0, 0); 509 if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes 510 thinkAirBrakes(pSprite->index); 511 aiNewState(pSprite, pXSprite, &gargoyleFSlash); 512 break; 513 case 0: 514 case 4: 515 break; 516 case 3: 517 if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleStone) 518 { 519 sfxPlay3DSound(pSprite, 1406, 0, 0); 520 if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes 521 thinkAirBrakes(pSprite->index); 522 aiNewState(pSprite, pXSprite, &gargoyleFSlash); 523 } 524 break; 525 default: 526 sfxPlay3DSound(pSprite, 1406, 0, 0); 527 if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes 528 thinkAirBrakes(pSprite->index); 529 aiNewState(pSprite, pXSprite, &gargoyleFSlash); 530 break; 531 } 532 } 533 else if ((height2-height > 0x2000 || floorZ-bottom > 0x2000) && nDist < 0x1400 && nDist > 0xa00) 534 { 535 aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1); 536 aiNewState(pSprite, pXSprite, &gargoyleSwoop); 537 } 538 else if ((height2-height < 0x2000 || floorZ-bottom < 0x2000) && klabs(nDeltaAngle) < 85) 539 aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1); 540 break; 541 case kDudeGargoyleStone: 542 if (nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85) 543 { 544 int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget); 545 int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0); 546 switch (hit) 547 { 548 case -1: 549 sfxPlay3DSound(pSprite, 1457, 0, 0); 550 aiNewState(pSprite, pXSprite, &gargoyleSBlast); 551 break; 552 case 0: 553 case 4: 554 break; 555 case 3: 556 if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleFlesh) 557 { 558 sfxPlay3DSound(pSprite, 1457, 0, 0); 559 aiNewState(pSprite, pXSprite, &gargoyleSBlast); 560 } 561 break; 562 default: 563 sfxPlay3DSound(pSprite, 1457, 0, 0); 564 aiNewState(pSprite, pXSprite, &gargoyleSBlast); 565 break; 566 } 567 } 568 else if (nDist < 0x400 && klabs(nDeltaAngle) < 85) 569 { 570 int dz = thinkChaseGetTargetHeight(pSprite, pDudeInfo, pTarget); 571 int hit = HitScan(pSprite, pSprite->z, dx, dy, dz, CLIPMASK1, 0); 572 switch (hit) 573 { 574 case -1: 575 if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes 576 thinkAirBrakes(pSprite->index); 577 aiNewState(pSprite, pXSprite, &gargoyleFSlash); 578 break; 579 case 0: 580 case 4: 581 break; 582 case 3: 583 if (pSprite->type != sprite[gHitInfo.hitsprite].type && sprite[gHitInfo.hitsprite].type != kDudeGargoyleFlesh) 584 { 585 if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes 586 thinkAirBrakes(pSprite->index); 587 aiNewState(pSprite, pXSprite, &gargoyleFSlash); 588 } 589 break; 590 default: 591 if (IsPlayerSprite(pTarget) && (gPlayer[pTarget->type-kDudePlayer1].posture == 2)) // if crouching, hit the brakes 592 thinkAirBrakes(pSprite->index); 593 aiNewState(pSprite, pXSprite, &gargoyleFSlash); 594 break; 595 } 596 } 597 else if ((height2-height > 0x2000 || floorZ-bottom > 0x2000) && nDist < 0x1400 && nDist > 0x800) 598 { 599 if (pSprite->type == kDudeGargoyleFlesh) 600 aiPlay3DSound(pSprite, 1400, AI_SFX_PRIORITY_1, -1); 601 else 602 aiPlay3DSound(pSprite, 1450, AI_SFX_PRIORITY_1, -1); 603 aiNewState(pSprite, pXSprite, &gargoyleSwoop); 604 } 605 else if ((height2-height < 0x2000 || floorZ-bottom > 0x2000) && klabs(nDeltaAngle) < 85) 606 aiPlay3DSound(pSprite, 1450, AI_SFX_PRIORITY_1, -1); 607 break; 608 } 609 } 610 return; 611 } 612 else 613 { 614 aiNewState(pSprite, pXSprite, &gargoyleFly); 615 return; 616 } 617 } 618 619 aiNewState(pSprite, pXSprite, &gargoyleFGoto); 620 pXSprite->target = -1; 621 } 622 623 static void entryFStatue(spritetype *pSprite, XSPRITE *pXSprite) 624 { 625 DUDEINFO *pDudeInfo = &dudeInfo[6]; 626 actHealDude(pXSprite, pDudeInfo->startHealth, pDudeInfo->startHealth); 627 pSprite->type = kDudeGargoyleFlesh; 628 } 629 630 static void entrySStatue(spritetype *pSprite, XSPRITE *pXSprite) 631 { 632 DUDEINFO *pDudeInfo = &dudeInfo[7]; 633 actHealDude(pXSprite, pDudeInfo->startHealth, pDudeInfo->startHealth); 634 pSprite->type = kDudeGargoyleStone; 635 } 636 637 static void MoveForward(spritetype *pSprite, XSPRITE *pXSprite) 638 { 639 int nSprite = pSprite->index; 640 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 641 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 642 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 643 return; 644 } 645 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 646 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 647 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 648 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 649 int nAccel = pDudeInfo->frontSpeed<<2; 650 if (klabs(nAng) > 341) 651 return; 652 if (pXSprite->target == -1) 653 pSprite->ang = (pSprite->ang+256)&2047; 654 int dx = pXSprite->targetX-pSprite->x; 655 int dy = pXSprite->targetY-pSprite->y; 656 int UNUSED(nAngle) = getangle(dx, dy); 657 int nDist = approxDist(dx, dy); 658 if ((unsigned int)Random(64) < 32 && nDist <= 0x400) 659 return; 660 int nCos = Cos(pSprite->ang); 661 int nSin = Sin(pSprite->ang); 662 int vx = xvel[nSprite]; 663 int vy = yvel[nSprite]; 664 int t1 = dmulscale30(vx, nCos, vy, nSin); 665 int t2 = dmulscale30(vx, nSin, -vy, nCos); 666 if (pXSprite->target == -1) 667 t1 += nAccel; 668 else 669 t1 += nAccel>>1; 670 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 671 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 672 } 673 674 static void MoveSlow(spritetype *pSprite, XSPRITE *pXSprite) 675 { 676 int nSprite = pSprite->index; 677 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 678 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 679 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 680 return; 681 } 682 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 683 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 684 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 685 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 686 int nAccel = pDudeInfo->frontSpeed<<2; 687 if (klabs(nAng) > 341) 688 { 689 pXSprite->goalAng = (pSprite->ang+512)&2047; 690 return; 691 } 692 int dx = pXSprite->targetX-pSprite->x; 693 int dy = pXSprite->targetY-pSprite->y; 694 int UNUSED(nAngle) = getangle(dx, dy); 695 int nDist = approxDist(dx, dy); 696 if (Chance(0x600) && nDist <= 0x400) 697 return; 698 int nCos = Cos(pSprite->ang); 699 int nSin = Sin(pSprite->ang); 700 int vx = xvel[nSprite]; 701 int vy = yvel[nSprite]; 702 int t1 = dmulscale30(vx, nCos, vy, nSin); 703 int t2 = dmulscale30(vx, nSin, -vy, nCos); 704 t1 = nAccel>>1; 705 t2 >>= 1; 706 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 707 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 708 switch (pSprite->type) { 709 case kDudeGargoyleFlesh: 710 zvel[nSprite] = 0x44444; 711 break; 712 case kDudeGargoyleStone: 713 zvel[nSprite] = 0x35555; 714 break; 715 } 716 } 717 718 static void MoveSwoop(spritetype *pSprite, XSPRITE *pXSprite) 719 { 720 int nSprite = pSprite->index; 721 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 722 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 723 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 724 return; 725 } 726 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 727 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 728 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 729 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 730 int nAccel = pDudeInfo->frontSpeed<<2; 731 if (klabs(nAng) > 341) 732 { 733 pXSprite->goalAng = (pSprite->ang+512)&2047; 734 return; 735 } 736 int dx = pXSprite->targetX-pSprite->x; 737 int dy = pXSprite->targetY-pSprite->y; 738 int UNUSED(nAngle) = getangle(dx, dy); 739 int nDist = approxDist(dx, dy); 740 if (Chance(0x600) && nDist <= 0x400) 741 return; 742 int nCos = Cos(pSprite->ang); 743 int nSin = Sin(pSprite->ang); 744 int vx = xvel[nSprite]; 745 int vy = yvel[nSprite]; 746 int t1 = dmulscale30(vx, nCos, vy, nSin); 747 int t2 = dmulscale30(vx, nSin, -vy, nCos); 748 t1 += nAccel>>1; 749 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 750 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 751 switch (pSprite->type) { 752 case kDudeGargoyleFlesh: 753 zvel[nSprite] = t1; 754 break; 755 case kDudeGargoyleStone: 756 zvel[nSprite] = t1; 757 break; 758 } 759 } 760 761 static void MoveFly(spritetype *pSprite, XSPRITE *pXSprite) 762 { 763 int nSprite = pSprite->index; 764 ///dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 765 if (!(pSprite->type >= kDudeBase && pSprite->type < kDudeMax)) { 766 consoleSysMsg("pSprite->type >= kDudeBase && pSprite->type < kDudeMax"); 767 return; 768 } 769 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 770 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 771 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 772 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 773 int nAccel = pDudeInfo->frontSpeed<<2; 774 if (klabs(nAng) > 341) 775 { 776 pSprite->ang = (pSprite->ang+512)&2047; 777 return; 778 } 779 int dx = pXSprite->targetX-pSprite->x; 780 int dy = pXSprite->targetY-pSprite->y; 781 int UNUSED(nAngle) = getangle(dx, dy); 782 int nDist = approxDist(dx, dy); 783 if (Chance(0x4000) && nDist <= 0x400) 784 return; 785 int nCos = Cos(pSprite->ang); 786 int nSin = Sin(pSprite->ang); 787 int vx = xvel[nSprite]; 788 int vy = yvel[nSprite]; 789 int t1 = dmulscale30(vx, nCos, vy, nSin); 790 int t2 = dmulscale30(vx, nSin, -vy, nCos); 791 t1 += nAccel>>1; 792 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 793 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 794 switch (pSprite->type) { 795 case kDudeGargoyleFlesh: 796 zvel[nSprite] = -t1; 797 break; 798 case kDudeGargoyleStone: 799 zvel[nSprite] = -t1; 800 break; 801 } 802 klabs(zvel[nSprite]); 803 }