aibat.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 "aibat.h" 32 #include "blood.h" 33 #include "db.h" 34 #include "dude.h" 35 #include "player.h" 36 #include "seq.h" 37 #include "trig.h" 38 39 static void BiteSeqCallback(int, int); 40 static void thinkTarget(spritetype *, XSPRITE *); 41 static void thinkSearch(spritetype *, XSPRITE *); 42 static void thinkGoto(spritetype *, XSPRITE *); 43 static void thinkPonder(spritetype *, XSPRITE *); 44 static void MoveDodgeUp(spritetype *, XSPRITE *); 45 static void MoveDodgeDown(spritetype *, XSPRITE *); 46 static void thinkChase(spritetype *, XSPRITE *); 47 static void MoveForward(spritetype *, XSPRITE *); 48 static void MoveSwoop(spritetype *, XSPRITE *); 49 static void MoveFly(spritetype *, XSPRITE *); 50 static void MoveToCeil(spritetype *, XSPRITE *); 51 52 static int nBiteClient = seqRegisterClient(BiteSeqCallback); 53 54 AISTATE batIdle = {kAiStateIdle, 0, -1, 0, NULL, NULL, thinkTarget, NULL }; 55 AISTATE batFlyIdle = {kAiStateIdle, 6, -1, 0, NULL, NULL, thinkTarget, NULL }; 56 AISTATE batChase = {kAiStateChase, 6, -1, 0, NULL, MoveForward, thinkChase, &batFlyIdle }; 57 AISTATE batPonder = {kAiStateOther, 6, -1, 0, NULL, NULL, thinkPonder, NULL }; 58 AISTATE batGoto = {kAiStateMove, 6, -1, 600, NULL, MoveForward, thinkGoto, &batFlyIdle }; 59 AISTATE batBite = {kAiStateChase, 7, nBiteClient, 60, NULL, NULL, NULL, &batPonder }; 60 AISTATE batRecoil = {kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &batChase }; 61 AISTATE batSearch = {kAiStateSearch, 6, -1, 120, NULL, MoveForward, thinkSearch, &batFlyIdle }; 62 AISTATE batSwoop = {kAiStateOther, 6, -1, 60, NULL, MoveSwoop, thinkChase, &batChase }; 63 AISTATE batFly = { kAiStateMove, 6, -1, 0, NULL, MoveFly, thinkChase, &batChase }; 64 AISTATE batTurn = {kAiStateMove, 6, -1, 60, NULL, aiMoveTurn, NULL, &batChase }; 65 AISTATE batHide = {kAiStateOther, 6, -1, 0, NULL, MoveToCeil, MoveForward, NULL }; 66 AISTATE batDodgeUp = {kAiStateMove, 6, -1, 120, NULL, MoveDodgeUp, 0, &batChase }; 67 AISTATE batDodgeUpRight = {kAiStateMove, 6, -1, 90, NULL, MoveDodgeUp, 0, &batChase }; 68 AISTATE batDodgeUpLeft = {kAiStateMove, 6, -1, 90, NULL, MoveDodgeUp, 0, &batChase }; 69 AISTATE batDodgeDown = {kAiStateMove, 6, -1, 120, NULL, MoveDodgeDown, 0, &batChase }; 70 AISTATE batDodgeDownRight = {kAiStateMove, 6, -1, 90, NULL, MoveDodgeDown, 0, &batChase }; 71 AISTATE batDodgeDownLeft = {kAiStateMove, 6, -1, 90, NULL, MoveDodgeDown, 0, &batChase }; 72 73 static void BiteSeqCallback(int, int nXSprite) 74 { 75 XSPRITE *pXSprite = &xsprite[nXSprite]; 76 spritetype *pSprite = &sprite[pXSprite->reference]; 77 /* 78 * workaround for 79 * pXSprite->target >= 0 && pXSprite->target < kMaxSprites in file NBlood/source/blood/src/aibat.cpp at line 85 80 * The value of pXSprite->target is -1. 81 * copied from thinkPonder() 82 * resolves this case, but may cause other issues? 83 */ 84 if (pXSprite->target == -1) 85 { 86 aiNewState(pSprite, pXSprite, &batSearch); 87 return; 88 } 89 90 spritetype *pTarget = &sprite[pXSprite->target]; 91 int dx = Cos(pSprite->ang) >> 16; 92 int dy = Sin(pSprite->ang) >> 16; 93 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 94 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 95 DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type); 96 int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2; 97 int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2; 98 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 99 actFireVector(pSprite, 0, 0, dx, dy, height2-height, kVectorBatBite); 100 } 101 102 static void thinkTarget(spritetype *pSprite, XSPRITE *pXSprite) 103 { 104 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 105 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 106 DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats; 107 if (pDudeExtraE->active && pDudeExtraE->thinkTime < 10) 108 pDudeExtraE->thinkTime++; 109 else if (pDudeExtraE->thinkTime >= 10 && pDudeExtraE->active) 110 { 111 pDudeExtraE->thinkTime = 0; 112 pXSprite->goalAng += 256; 113 POINT3D *pTarget = &baseSprite[pSprite->index]; 114 aiSetTarget(pXSprite, pTarget->x, pTarget->y, pTarget->z); 115 aiNewState(pSprite, pXSprite, &batTurn); 116 return; 117 } 118 if (Chance(pDudeInfo->alertChance)) 119 { 120 for (int p = connecthead; p >= 0; p = connectpoint2[p]) 121 { 122 PLAYER *pPlayer = &gPlayer[p]; 123 if (pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0) 124 continue; 125 int x = pPlayer->pSprite->x; 126 int y = pPlayer->pSprite->y; 127 int z = pPlayer->pSprite->z; 128 int nSector = pPlayer->pSprite->sectnum; 129 int dx = x-pSprite->x; 130 int dy = y-pSprite->y; 131 int nDist = approxDist(dx, dy); 132 if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist) 133 continue; 134 if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum)) 135 continue; 136 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 137 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 138 { 139 aiSetTarget(pXSprite, pPlayer->nSprite); 140 aiActivateDude(pSprite, pXSprite); 141 } 142 else if (nDist < pDudeInfo->hearDist) 143 { 144 aiSetTarget(pXSprite, x, y, z); 145 aiActivateDude(pSprite, pXSprite); 146 } 147 else 148 continue; 149 break; 150 } 151 } 152 } 153 154 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite) 155 { 156 aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); 157 thinkTarget(pSprite, pXSprite); 158 } 159 160 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite) 161 { 162 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 163 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 164 int dx = pXSprite->targetX-pSprite->x; 165 int dy = pXSprite->targetY-pSprite->y; 166 int nAngle = getangle(dx, dy); 167 int nDist = approxDist(dx, dy); 168 aiChooseDirection(pSprite, pXSprite, nAngle); 169 if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery) 170 aiNewState(pSprite, pXSprite, &batSearch); 171 thinkTarget(pSprite, pXSprite); 172 } 173 174 static void thinkPonder(spritetype *pSprite, XSPRITE *pXSprite) 175 { 176 if (pXSprite->target == -1) 177 { 178 aiNewState(pSprite, pXSprite, &batSearch); 179 return; 180 } 181 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 182 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 183 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 184 spritetype *pTarget = &sprite[pXSprite->target]; 185 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 186 int dx = pTarget->x-pSprite->x; 187 int dy = pTarget->y-pSprite->y; 188 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 189 if (pXTarget->health == 0) 190 { 191 aiNewState(pSprite, pXSprite, &batSearch); 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 int height2 = (getDudeInfo(pTarget->type)->eyeHeight*pTarget->yrepeat)<<2; 200 int top, bottom; 201 GetSpriteExtents(pSprite, &top, &bottom); 202 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 203 { 204 aiSetTarget(pXSprite, pXSprite->target); 205 if (height2-height < 0x3000 && nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85) 206 aiNewState(pSprite, pXSprite, &batDodgeUp); 207 else if (height2-height > 0x5000 && nDist < 0x1800 && nDist > 0xc00 && klabs(nDeltaAngle) < 85) 208 aiNewState(pSprite, pXSprite, &batDodgeDown); 209 else if (height2-height < 0x2000 && nDist < 0x200 && klabs(nDeltaAngle) < 85) 210 aiNewState(pSprite, pXSprite, &batDodgeUp); 211 else if (height2-height > 0x6000 && nDist < 0x1400 && nDist > 0x800 && klabs(nDeltaAngle) < 85) 212 aiNewState(pSprite, pXSprite, &batDodgeDown); 213 else if (height2-height < 0x2000 && nDist < 0x1400 && nDist > 0x800 && klabs(nDeltaAngle) < 85) 214 aiNewState(pSprite, pXSprite, &batDodgeUp); 215 else if (height2-height < 0x2000 && klabs(nDeltaAngle) < 85 && nDist > 0x1400) 216 aiNewState(pSprite, pXSprite, &batDodgeUp); 217 else if (height2-height > 0x4000) 218 aiNewState(pSprite, pXSprite, &batDodgeDown); 219 else 220 aiNewState(pSprite, pXSprite, &batDodgeUp); 221 return; 222 } 223 } 224 aiNewState(pSprite, pXSprite, &batGoto); 225 pXSprite->target = -1; 226 } 227 228 static void MoveDodgeUp(spritetype *pSprite, XSPRITE *pXSprite) 229 { 230 int nSprite = pSprite->index; 231 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 232 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 233 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 234 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 235 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 236 int nCos = Cos(pSprite->ang); 237 int nSin = Sin(pSprite->ang); 238 int dx = xvel[nSprite]; 239 int dy = yvel[nSprite]; 240 int t1 = dmulscale30(dx, nCos, dy, nSin); 241 int t2 = dmulscale30(dx, nSin, -dy, nCos); 242 if (pXSprite->dodgeDir > 0) 243 t2 += pDudeInfo->sideSpeed; 244 else 245 t2 -= pDudeInfo->sideSpeed; 246 247 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 248 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 249 zvel[nSprite] = -0x52aaa; 250 } 251 252 static void MoveDodgeDown(spritetype *pSprite, XSPRITE *pXSprite) 253 { 254 int nSprite = pSprite->index; 255 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 256 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 257 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 258 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 259 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 260 if (pXSprite->dodgeDir == 0) 261 return; 262 int nCos = Cos(pSprite->ang); 263 int nSin = Sin(pSprite->ang); 264 int dx = xvel[nSprite]; 265 int dy = yvel[nSprite]; 266 int t1 = dmulscale30(dx, nCos, dy, nSin); 267 int t2 = dmulscale30(dx, nSin, -dy, nCos); 268 if (pXSprite->dodgeDir > 0) 269 t2 += pDudeInfo->sideSpeed; 270 else 271 t2 -= pDudeInfo->sideSpeed; 272 273 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 274 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 275 zvel[nSprite] = 0x44444; 276 } 277 278 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite) 279 { 280 if (pXSprite->target == -1) 281 { 282 aiNewState(pSprite, pXSprite, &batGoto); 283 return; 284 } 285 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 286 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 287 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 288 spritetype *pTarget = &sprite[pXSprite->target]; 289 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 290 int dx = pTarget->x-pSprite->x; 291 int dy = pTarget->y-pSprite->y; 292 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 293 if (pXTarget->health == 0) 294 { 295 aiNewState(pSprite, pXSprite, &batSearch); 296 return; 297 } 298 if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type - kDudePlayer1], kPwUpShadowCloak) > 0) 299 { 300 aiNewState(pSprite, pXSprite, &batSearch); 301 return; 302 } 303 int nDist = approxDist(dx, dy); 304 if (nDist <= pDudeInfo->seeDist) 305 { 306 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 307 int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2; 308 // Should be dudeInfo[pTarget->type-kDudeBase] 309 int height2 = (pDudeInfo->eyeHeight*pTarget->yrepeat)<<2; 310 int top, bottom; 311 GetSpriteExtents(pSprite, &top, &bottom); 312 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 313 { 314 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 315 { 316 aiSetTarget(pXSprite, pXSprite->target); 317 int floorZ = getflorzofslope(pSprite->sectnum, pSprite->x, pSprite->y); 318 if (height2-height < 0x2000 && nDist < 0x200 && klabs(nDeltaAngle) < 85) 319 aiNewState(pSprite, pXSprite, &batBite); 320 else if ((height2-height > 0x5000 || floorZ-bottom > 0x5000) && nDist < 0x1400 && nDist > 0x800 && klabs(nDeltaAngle) < 85) 321 aiNewState(pSprite, pXSprite, &batSwoop); 322 else if ((height2-height < 0x3000 || floorZ-bottom < 0x3000) && klabs(nDeltaAngle) < 85) 323 aiNewState(pSprite, pXSprite, &batFly); 324 return; 325 } 326 } 327 else 328 { 329 aiNewState(pSprite, pXSprite, &batFly); 330 return; 331 } 332 } 333 334 pXSprite->target = -1; 335 aiNewState(pSprite, pXSprite, &batHide); 336 } 337 338 static void MoveForward(spritetype *pSprite, XSPRITE *pXSprite) 339 { 340 int nSprite = pSprite->index; 341 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 342 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 343 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 344 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 345 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 346 int nAccel = pDudeInfo->frontSpeed<<2; 347 if (klabs(nAng) > 341) 348 return; 349 if (pXSprite->target == -1) 350 pSprite->ang = (pSprite->ang+256)&2047; 351 int dx = pXSprite->targetX-pSprite->x; 352 int dy = pXSprite->targetY-pSprite->y; 353 int UNUSED(nAngle) = getangle(dx, dy); 354 int nDist = approxDist(dx, dy); 355 if ((unsigned int)Random(64) < 32 && nDist <= 0x200) 356 return; 357 int nCos = Cos(pSprite->ang); 358 int nSin = Sin(pSprite->ang); 359 int vx = xvel[nSprite]; 360 int vy = yvel[nSprite]; 361 int t1 = dmulscale30(vx, nCos, vy, nSin); 362 int t2 = dmulscale30(vx, nSin, -vy, nCos); 363 if (pXSprite->target == -1) 364 t1 += nAccel; 365 else 366 t1 += nAccel>>1; 367 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 368 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 369 } 370 371 static void MoveSwoop(spritetype *pSprite, XSPRITE *pXSprite) 372 { 373 int nSprite = pSprite->index; 374 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 375 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 376 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 377 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 378 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 379 int nAccel = pDudeInfo->frontSpeed<<2; 380 if (klabs(nAng) > 341) 381 { 382 pXSprite->goalAng = (pSprite->ang+512)&2047; 383 return; 384 } 385 int dx = pXSprite->targetX-pSprite->x; 386 int dy = pXSprite->targetY-pSprite->y; 387 int UNUSED(nAngle) = getangle(dx, dy); 388 int nDist = approxDist(dx, dy); 389 if (Chance(0x600) && nDist <= 0x200) 390 return; 391 int nCos = Cos(pSprite->ang); 392 int nSin = Sin(pSprite->ang); 393 int vx = xvel[nSprite]; 394 int vy = yvel[nSprite]; 395 int t1 = dmulscale30(vx, nCos, vy, nSin); 396 int t2 = dmulscale30(vx, nSin, -vy, nCos); 397 t1 += nAccel>>1; 398 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 399 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 400 zvel[nSprite] = 0x44444; 401 } 402 403 static void MoveFly(spritetype *pSprite, XSPRITE *pXSprite) 404 { 405 int nSprite = pSprite->index; 406 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 407 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 408 int nAng = ((pXSprite->goalAng+1024-pSprite->ang)&2047)-1024; 409 int nTurnRange = (pDudeInfo->angSpeed<<2)>>4; 410 pSprite->ang = (pSprite->ang+ClipRange(nAng, -nTurnRange, nTurnRange))&2047; 411 int nAccel = pDudeInfo->frontSpeed<<2; 412 if (klabs(nAng) > 341) 413 { 414 pSprite->ang = (pSprite->ang+512)&2047; 415 return; 416 } 417 int dx = pXSprite->targetX-pSprite->x; 418 int dy = pXSprite->targetY-pSprite->y; 419 int UNUSED(nAngle) = getangle(dx, dy); 420 int nDist = approxDist(dx, dy); 421 if (Chance(0x4000) && nDist <= 0x200) 422 return; 423 int nCos = Cos(pSprite->ang); 424 int nSin = Sin(pSprite->ang); 425 int vx = xvel[nSprite]; 426 int vy = yvel[nSprite]; 427 int t1 = dmulscale30(vx, nCos, vy, nSin); 428 int t2 = dmulscale30(vx, nSin, -vy, nCos); 429 t1 += nAccel>>1; 430 xvel[nSprite] = dmulscale30(t1, nCos, t2, nSin); 431 yvel[nSprite] = dmulscale30(t1, nSin, -t2, nCos); 432 zvel[nSprite] = -0x2d555; 433 } 434 435 void MoveToCeil(spritetype *pSprite, XSPRITE *pXSprite) 436 { 437 int x = pSprite->x; 438 int y = pSprite->y; 439 int z = pSprite->z; 440 int nSector = pSprite->sectnum; 441 if (z - pXSprite->targetZ < 0x1000) 442 { 443 DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats; 444 pDudeExtraE->active = 0; 445 pSprite->flags = 0; 446 aiNewState(pSprite, pXSprite, &batIdle); 447 } 448 else 449 aiSetTarget(pXSprite, x, y, sector[nSector].ceilingz); 450 }