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