aicaleb.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 #include "actor.h" 30 #include "ai.h" 31 #include "aicaleb.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 SeqAttackCallback(int, int); 42 static void thinkSearch(spritetype *, XSPRITE *); 43 static void thinkGoto(spritetype *, XSPRITE *); 44 static void thinkChase(spritetype *, XSPRITE *); 45 static void thinkSwimGoto(spritetype *, XSPRITE *); 46 static void thinkSwimChase(spritetype *, XSPRITE *); 47 static void sub_65D04(spritetype *, XSPRITE *); 48 static void sub_65F44(spritetype *, XSPRITE *); 49 static void sub_661E0(spritetype *, XSPRITE *); 50 51 static int nAttackClient = seqRegisterClient(SeqAttackCallback); 52 53 AISTATE tinycalebIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, aiThinkTarget, NULL }; 54 AISTATE tinycalebChase = { kAiStateChase, 6, -1, 0, NULL, aiMoveForward, thinkChase, NULL }; 55 AISTATE tinycalebDodge = { kAiStateMove, 6, -1, 90, NULL, aiMoveDodge, NULL, &tinycalebChase }; 56 AISTATE tinycalebGoto = { kAiStateMove, 6, -1, 600, NULL, aiMoveForward, thinkGoto, &tinycalebIdle }; 57 AISTATE tinycalebAttack = { kAiStateChase, 0, nAttackClient, 120, NULL, NULL, NULL, &tinycalebChase }; 58 AISTATE tinycalebSearch = { kAiStateSearch, 6, -1, 120, NULL, aiMoveForward, thinkSearch, &tinycalebIdle }; 59 AISTATE tinycalebRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &tinycalebDodge }; 60 AISTATE tinycalebTeslaRecoil = { kAiStateRecoil, 4, -1, 0, NULL, NULL, NULL, &tinycalebDodge }; 61 AISTATE tinycalebSwimIdle = { kAiStateIdle, 10, -1, 0, NULL, NULL, aiThinkTarget, NULL }; 62 AISTATE tinycalebSwimChase = { kAiStateChase, 8, -1, 0, NULL, sub_65D04, thinkSwimChase, NULL }; 63 AISTATE tinycalebSwimDodge = { kAiStateMove, 8, -1, 90, NULL, aiMoveDodge, NULL, &tinycalebSwimChase }; 64 AISTATE tinycalebSwimGoto = { kAiStateMove, 8, -1, 600, NULL, aiMoveForward, thinkSwimGoto, &tinycalebSwimIdle }; 65 AISTATE tinycalebSwimSearch = { kAiStateSearch, 8, -1, 120, NULL, aiMoveForward, thinkSearch, &tinycalebSwimIdle }; 66 AISTATE tinycalebSwimAttack = { kAiStateChase, 10, nAttackClient, 0, NULL, NULL, thinkSwimChase, &tinycalebSwimChase }; 67 AISTATE tinycalebSwimRecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &tinycalebSwimDodge }; 68 AISTATE tinycaleb139660 = { kAiStateOther, 8, -1, 120, NULL, sub_65F44, thinkSwimChase, &tinycalebSwimChase }; 69 AISTATE tinycaleb13967C = { kAiStateOther, 8, -1, 0, NULL, sub_661E0, thinkSwimChase, &tinycalebSwimChase }; 70 AISTATE tinycaleb139698 = { kAiStateOther, 8, -1, 120, NULL, aiMoveTurn, NULL, &tinycalebSwimChase }; 71 72 static void SeqAttackCallback(int, int nXSprite) 73 { 74 XSPRITE *pXSprite = &xsprite[nXSprite]; 75 int nSprite = xsprite[nXSprite].reference; 76 spritetype *pSprite = &sprite[nSprite]; 77 int dx = Cos(pSprite->ang)>>16; 78 int dy = Sin(pSprite->ang)>>16; 79 int dz = gDudeSlope[nXSprite]; 80 dx += Random2(1500); 81 dy += Random2(1500); 82 dz += Random2(1500); 83 bool useProjectile = gGameOptions.nHitscanProjectiles && !VanillaMode(); // if enemy hitscan projectiles are enabled, spawn bullet projectile 84 if (useProjectile && spriRangeIsFine(pXSprite->target)) // if target is valid 85 { 86 const spritetype *pTarget = &sprite[pXSprite->target]; 87 if (klabs(pSprite->z-pTarget->z) < 30000) // if height difference is under 30000, check target distance 88 useProjectile = approxDist(pSprite->x-pTarget->x, pSprite->y-pTarget->y) > 1250; // if target is very close, just use hitscan 89 } 90 for (int i = 0; i < 2; i++) 91 { 92 int r1 = Random3(500); 93 int r2 = Random3(1000); 94 int r3 = Random3(1000); 95 if (!useProjectile) // normal hitscan 96 actFireVector(pSprite, 0, 0, dx+r3, dy+r2, dz+r1, kVectorShell); 97 else // projectile 98 actFireMissile(pSprite, 0, 0, dx+r3, dy+r2, dz+r1, kMissileBullet); 99 } 100 if (Chance(0x8000)) 101 sfxPlay3DSound(pSprite, 10000+Random(5), -1, 0); 102 if (Chance(0x8000)) 103 sfxPlay3DSound(pSprite, 1001, -1, 0); 104 else 105 sfxPlay3DSound(pSprite, 1002, -1, 0); 106 } 107 108 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite) 109 { 110 aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); 111 aiThinkTarget(pSprite, pXSprite); 112 } 113 114 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite) 115 { 116 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 117 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 118 XSECTOR *pXSector; 119 int nXSector = sector[pSprite->sectnum].extra; 120 if (nXSector > 0) 121 pXSector = &xsector[nXSector]; 122 else 123 pXSector = NULL; 124 int dx = pXSprite->targetX-pSprite->x; 125 int dy = pXSprite->targetY-pSprite->y; 126 int nAngle = getangle(dx, dy); 127 int nDist = approxDist(dx, dy); 128 aiChooseDirection(pSprite, pXSprite, nAngle); 129 if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery) 130 { 131 if (pXSector && pXSector->Underwater) 132 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 133 else 134 aiNewState(pSprite, pXSprite, &tinycalebSearch); 135 } 136 aiThinkTarget(pSprite, pXSprite); 137 } 138 139 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite) 140 { 141 if (pXSprite->target == -1) 142 { 143 XSECTOR *pXSector; 144 int nXSector = sector[pSprite->sectnum].extra; 145 if (nXSector > 0) 146 pXSector = &xsector[nXSector]; 147 else 148 pXSector = NULL; 149 if (pXSector && pXSector->Underwater) 150 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 151 else 152 aiNewState(pSprite, pXSprite, &tinycalebSearch); 153 return; 154 } 155 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 156 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 157 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 158 spritetype *pTarget = &sprite[pXSprite->target]; 159 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 160 int dx = pTarget->x-pSprite->x; 161 int dy = pTarget->y-pSprite->y; 162 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 163 if (pXTarget->health == 0) 164 { 165 XSECTOR *pXSector; 166 int nXSector = sector[pSprite->sectnum].extra; 167 if (nXSector > 0) 168 pXSector = &xsector[nXSector]; 169 else 170 pXSector = NULL; 171 if (pXSector && pXSector->Underwater) 172 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 173 else 174 { 175 aiPlay3DSound(pSprite, 11000+Random(4), AI_SFX_PRIORITY_1, -1); 176 aiNewState(pSprite, pXSprite, &tinycalebSearch); 177 } 178 return; 179 } 180 if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0) 181 { 182 XSECTOR *pXSector; 183 int nXSector = sector[pSprite->sectnum].extra; 184 if (nXSector > 0) 185 pXSector = &xsector[nXSector]; 186 else 187 pXSector = NULL; 188 if (pXSector && pXSector->Underwater) 189 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 190 else 191 aiNewState(pSprite, pXSprite, &tinycalebSearch); 192 return; 193 } 194 int nDist = approxDist(dx, dy); 195 if (nDist <= pDudeInfo->seeDist) 196 { 197 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 198 int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2; 199 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 200 { 201 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 202 { 203 aiSetTarget(pXSprite, pXSprite->target); 204 int nXSprite = sprite[pXSprite->reference].extra; 205 gDudeSlope[nXSprite] = divscale10(pTarget->z-pSprite->z, nDist); 206 if (nDist < 0x599 && klabs(nDeltaAngle) < 28) 207 { 208 XSECTOR *pXSector; 209 int nXSector = sector[pSprite->sectnum].extra; 210 if (nXSector > 0) 211 pXSector = &xsector[nXSector]; 212 else 213 pXSector = NULL; 214 int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0); 215 switch (hit) 216 { 217 case -1: 218 if (pXSector && pXSector->Underwater) 219 aiNewState(pSprite, pXSprite, &tinycalebSwimAttack); 220 else 221 aiNewState(pSprite, pXSprite, &tinycalebAttack); 222 break; 223 case 3: 224 if (pSprite->type != sprite[gHitInfo.hitsprite].type) 225 { 226 if (pXSector && pXSector->Underwater) 227 aiNewState(pSprite, pXSprite, &tinycalebSwimAttack); 228 else 229 aiNewState(pSprite, pXSprite, &tinycalebAttack); 230 } 231 else 232 { 233 if (pXSector && pXSector->Underwater) 234 aiNewState(pSprite, pXSprite, &tinycalebSwimDodge); 235 else 236 aiNewState(pSprite, pXSprite, &tinycalebDodge); 237 } 238 break; 239 default: 240 if (pXSector && pXSector->Underwater) 241 aiNewState(pSprite, pXSprite, &tinycalebSwimAttack); 242 else 243 aiNewState(pSprite, pXSprite, &tinycalebAttack); 244 break; 245 } 246 } 247 } 248 return; 249 } 250 } 251 252 XSECTOR *pXSector; 253 int nXSector = sector[pSprite->sectnum].extra; 254 if (nXSector > 0) 255 pXSector = &xsector[nXSector]; 256 else 257 pXSector = NULL; 258 if (pXSector && pXSector->Underwater) 259 aiNewState(pSprite, pXSprite, &tinycalebSwimGoto); 260 else 261 aiNewState(pSprite, pXSprite, &tinycalebGoto); 262 if (Chance(0x2000)) 263 sfxPlay3DSound(pSprite, 10000 + Random(5), -1, 0); 264 pXSprite->target = -1; 265 } 266 267 static void thinkSwimGoto(spritetype *pSprite, XSPRITE *pXSprite) 268 { 269 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 270 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 271 int dx = pXSprite->targetX-pSprite->x; 272 int dy = pXSprite->targetY-pSprite->y; 273 int nAngle = getangle(dx, dy); 274 int nDist = approxDist(dx, dy); 275 aiChooseDirection(pSprite, pXSprite, nAngle); 276 if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery) 277 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 278 aiThinkTarget(pSprite, pXSprite); 279 } 280 281 static void thinkSwimChase(spritetype *pSprite, XSPRITE *pXSprite) 282 { 283 if (pXSprite->target == -1) 284 { 285 aiNewState(pSprite, pXSprite, &tinycalebSwimGoto); 286 return; 287 } 288 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 289 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 290 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 291 spritetype *pTarget = &sprite[pXSprite->target]; 292 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 293 int dx = pTarget->x-pSprite->x; 294 int dy = pTarget->y-pSprite->y; 295 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 296 if (pXTarget->health == 0) 297 { 298 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 299 return; 300 } 301 if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0) 302 { 303 aiNewState(pSprite, pXSprite, &tinycalebSwimSearch); 304 return; 305 } 306 int nDist = approxDist(dx, dy); 307 if (nDist <= pDudeInfo->seeDist) 308 { 309 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 310 int height = pDudeInfo->eyeHeight+pSprite->z; 311 int top, bottom; 312 GetSpriteExtents(pSprite, &top, &bottom); 313 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 314 { 315 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 316 { 317 aiSetTarget(pXSprite, pXSprite->target); 318 int UNUSED(floorZ) = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y); 319 if (nDist < 0x400 && klabs(nDeltaAngle) < 85) 320 aiNewState(pSprite, pXSprite, &tinycalebSwimAttack); 321 else 322 aiNewState(pSprite, pXSprite, &tinycaleb13967C); 323 } 324 } 325 else 326 aiNewState(pSprite, pXSprite, &tinycaleb13967C); 327 return; 328 } 329 aiNewState(pSprite, pXSprite, &tinycalebSwimGoto); 330 pXSprite->target = -1; 331 } 332 333 static void sub_65D04(spritetype *pSprite, XSPRITE *pXSprite) 334 { 335 int nSprite = pSprite->index; 336 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 337 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 338 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 339 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 340 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 341 int nAccel = pDudeInfo->frontSpeed<<2; 342 if (klabs(nAng) > 341) 343 return; 344 if (pXSprite->target == -1) 345 pSprite->ang = (pSprite->ang+256)&2047; 346 int dx = pXSprite->targetX-pSprite->x; 347 int dy = pXSprite->targetY-pSprite->y; 348 int UNUSED(nAngle) = getangle(dx, dy); 349 int nDist = approxDist(dx, dy); 350 if (Random(64) < 32 && nDist <= 0x400) 351 return; 352 int nCos = Cos(pSprite->ang); 353 int nSin = Sin(pSprite->ang); 354 int vx = xvel[nSprite]; 355 int vy = yvel[nSprite]; 356 int t1 = dmulscale30(vx, nCos, vy, nSin); 357 int t2 = dmulscale30(vx, nSin, -vy, nCos); 358 if (pXSprite->target == -1) 359 t1 += nAccel; 360 else 361 t1 += nAccel>>2; 362 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 363 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 364 } 365 366 static void sub_65F44(spritetype *pSprite, XSPRITE *pXSprite) 367 { 368 int nSprite = pSprite->index; 369 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 370 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 371 spritetype *pTarget = &sprite[pXSprite->target]; 372 int z = pSprite->z + getDudeInfo(pSprite->type)->eyeHeight; 373 int z2 = pTarget->z + getDudeInfo(pTarget->type)->eyeHeight; 374 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 375 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 376 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 377 int nAccel = pDudeInfo->frontSpeed<<2; 378 if (klabs(nAng) > 341) 379 { 380 pXSprite->goalAng = (pSprite->ang+512)&2047; 381 return; 382 } 383 int dx = pXSprite->targetX-pSprite->x; 384 int dy = pXSprite->targetY-pSprite->y; 385 int dz = z2 - z; 386 int UNUSED(nAngle) = getangle(dx, dy); 387 int nDist = approxDist(dx, dy); 388 if (Chance(0x600) && nDist <= 0x400) 389 return; 390 int nCos = Cos(pSprite->ang); 391 int nSin = Sin(pSprite->ang); 392 int vx = xvel[nSprite]; 393 int vy = yvel[nSprite]; 394 int t1 = dmulscale30(vx, nCos, vy, nSin); 395 int t2 = dmulscale30(vx, nSin, -vy, nCos); 396 t1 += nAccel; 397 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 398 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 399 zvel[nSprite] = -dz; 400 } 401 402 static void sub_661E0(spritetype *pSprite, XSPRITE *pXSprite) 403 { 404 int nSprite = pSprite->index; 405 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 406 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 407 spritetype *pTarget = &sprite[pXSprite->target]; 408 int z = pSprite->z + getDudeInfo(pSprite->type)->eyeHeight; 409 int z2 = pTarget->z + getDudeInfo(pTarget->type)->eyeHeight; 410 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 411 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 412 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 413 int nAccel = pDudeInfo->frontSpeed<<2; 414 if (klabs(nAng) > 341) 415 { 416 pSprite->ang = (pSprite->ang+512)&2047; 417 return; 418 } 419 int dx = pXSprite->targetX-pSprite->x; 420 int dy = pXSprite->targetY-pSprite->y; 421 int dz = (z2 - z)<<3; 422 int UNUSED(nAngle) = getangle(dx, dy); 423 int nDist = approxDist(dx, dy); 424 if (Chance(0x4000) && nDist <= 0x400) 425 return; 426 int nCos = Cos(pSprite->ang); 427 int nSin = Sin(pSprite->ang); 428 int vx = xvel[nSprite]; 429 int vy = yvel[nSprite]; 430 int t1 = dmulscale30(vx, nCos, vy, nSin); 431 int t2 = dmulscale30(vx, nSin, -vy, nCos); 432 t1 += nAccel>>1; 433 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 434 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 435 zvel[nSprite] = dz; 436 }