aispid.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 "aispid.h" 32 #include "blood.h" 33 #include "db.h" 34 #include "dude.h" 35 #include "endgame.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 43 static void SpidBiteSeqCallback(int, int); 44 static void SpidJumpSeqCallback(int, int); 45 static void SpidBirthSeqCallback(int, int); 46 static void thinkSearch(spritetype *, XSPRITE *); 47 static void thinkGoto(spritetype *, XSPRITE *); 48 static void thinkChase(spritetype *, XSPRITE *); 49 50 static int nBiteClient = seqRegisterClient(SpidBiteSeqCallback); 51 static int nJumpClient = seqRegisterClient(SpidJumpSeqCallback); 52 static int nBirthClient = seqRegisterClient(SpidBirthSeqCallback); 53 54 AISTATE spidIdle = { kAiStateIdle, 0, -1, 0, NULL, NULL, aiThinkTarget, NULL }; 55 AISTATE spidChase = { kAiStateChase, 7, -1, 0, NULL, aiMoveForward, thinkChase, NULL }; 56 AISTATE spidDodge = { kAiStateMove, 7, -1, 90, NULL, aiMoveDodge, NULL, &spidChase }; 57 AISTATE spidGoto = { kAiStateMove, 7, -1, 600, NULL, aiMoveForward, thinkGoto, &spidIdle }; 58 AISTATE spidSearch = { kAiStateSearch, 7, -1, 1800, NULL, aiMoveForward, thinkSearch, &spidIdle }; 59 AISTATE spidBite = { kAiStateChase, 6, nBiteClient, 60, NULL, NULL, NULL, &spidChase }; 60 AISTATE spidJump = { kAiStateChase, 8, nJumpClient, 60, NULL, aiMoveForward, NULL, &spidChase }; 61 AISTATE spidBirth = { kAiStateOther, 0, nBirthClient, 60, NULL, NULL, NULL, &spidIdle }; 62 63 static char SpidPoisonPlayer(XSPRITE *pXDude, int nBlind, int max) 64 { 65 dassert(pXDude != NULL); 66 int nDude = pXDude->reference; 67 spritetype *pDude = &sprite[nDude]; 68 if (IsPlayerSprite(pDude)) 69 { 70 nBlind <<= 4; 71 max <<= 4; 72 PLAYER *pPlayer = &gPlayer[pDude->type-kDudePlayer1]; 73 if (pPlayer->blindEffect < max) 74 { 75 pPlayer->blindEffect = ClipHigh(pPlayer->blindEffect+nBlind, max); 76 return 1; 77 } 78 } 79 return 0; 80 } 81 82 static void SpidBiteSeqCallback(int, int nXSprite) 83 { 84 XSPRITE *pXSprite = &xsprite[nXSprite]; 85 int nSprite = pXSprite->reference; 86 spritetype *pSprite = &sprite[nSprite]; 87 int dx = Cos(pSprite->ang)>>16; 88 int dy = Sin(pSprite->ang)>>16; 89 dx += Random2(2000); 90 dy += Random2(2000); 91 int dz = Random2(2000); 92 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 93 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 94 spritetype *pTarget = &sprite[pXSprite->target]; 95 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 96 if (IsPlayerSprite(pTarget)) { 97 98 int hit = HitScan(pSprite, pSprite->z, dx, dy, 0, CLIPMASK1, 0); 99 if (hit == 3 && IsPlayerSprite(&sprite[gHitInfo.hitsprite])) { 100 dz += pTarget->z - pSprite->z; 101 PLAYER *pPlayer = &gPlayer[pTarget->type - kDudePlayer1]; 102 switch (pSprite->type) { 103 case kDudeSpiderBrown: 104 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); 105 if (IsPlayerSprite(pTarget) && !pPlayer->godMode && powerupCheck(pPlayer, kPwUpDeathMask) <= 0 && Chance(0x4000)) 106 powerupActivate(pPlayer, kPwUpDeliriumShroom); 107 break; 108 case kDudeSpiderRed: 109 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); 110 if (Chance(0x5000)) SpidPoisonPlayer(pXTarget, 4, 16); 111 break; 112 case kDudeSpiderBlack: 113 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); 114 SpidPoisonPlayer(pXTarget, 8, 16); 115 break; 116 case kDudeSpiderMother: { 117 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); 118 119 dx += Random2(2000); 120 dy += Random2(2000); 121 dz += Random2(2000); 122 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorSpiderBite); 123 SpidPoisonPlayer(pXTarget, 8, 16); 124 break; 125 } 126 } 127 } 128 129 } 130 } 131 132 static void SpidJumpSeqCallback(int, int nXSprite) 133 { 134 XSPRITE *pXSprite = &xsprite[nXSprite]; 135 int nSprite = pXSprite->reference; 136 spritetype *pSprite = &sprite[nSprite]; 137 int dx = Cos(pSprite->ang)>>16; 138 int dy = Sin(pSprite->ang)>>16; 139 dx += Random2(200); 140 dy += Random2(200); 141 int dz = Random2(200); 142 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 143 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 144 spritetype *pTarget = &sprite[pXSprite->target]; 145 if (IsPlayerSprite(pTarget)) { 146 dz += pTarget->z-pSprite->z; 147 switch (pSprite->type) { 148 case kDudeSpiderBrown: 149 case kDudeSpiderBlack: 150 xvel[nSprite] = dx << 16; 151 yvel[nSprite] = dy << 16; 152 zvel[nSprite] = dz << 16; 153 break; 154 case kDudeSpiderRed: 155 break; 156 } 157 } 158 } 159 160 static void SpidBirthSeqCallback(int, int nXSprite) 161 { 162 XSPRITE *pXSprite = &xsprite[nXSprite]; 163 int nSprite = pXSprite->reference; 164 spritetype *pSprite = &sprite[nSprite]; 165 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 166 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 167 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 168 spritetype *pTarget = &sprite[pXSprite->target]; 169 DUDEEXTRA_STATS *pDudeExtraE = &gDudeExtra[pSprite->extra].stats; 170 int dx = pXSprite->targetX-pSprite->x; 171 int dy = pXSprite->targetY-pSprite->y; 172 int nAngle = getangle(dx, dy); 173 int nDist = approxDist(dx, dy); 174 175 spritetype *pSpawn = NULL; 176 if (IsPlayerSprite(pTarget) && pDudeExtraE->birthCounter < 10) { 177 if (klabs(pSprite->ang-nAngle) < pDudeInfo->periphery) { 178 if (nDist < 0x1a00 && nDist > 0x1400) 179 pSpawn = actSpawnDude(pSprite, kDudeSpiderRed, pSprite->clipdist, 0); 180 else if ((nDist < 0xc00) || (nDist < 0x1400 && nDist > 0xc00)) 181 pSpawn = actSpawnDude(pSprite, kDudeSpiderBrown, pSprite->clipdist, 0); 182 } 183 184 if (pSpawn) { 185 if (gGameOptions.nRandomizerMode && !VanillaMode()) 186 dbRandomizerMode(pSpawn); 187 if (dbIsBannedSprite(pSpawn, NULL)) { // if spawned sprite is banned, remove sprite 188 evKill(pSpawn->index, 3); 189 if (sprite[pSpawn->index].extra > 0) 190 seqKill(3, sprite[pSpawn->index].extra); 191 DeleteSprite(pSpawn->index); 192 return; 193 } 194 pDudeExtraE->birthCounter++; 195 if ((pSpawn->type == kDudeSpiderRed) || (pSpawn->type == kDudeSpiderBrown) || VanillaMode()) 196 pSpawn->owner = nSprite; 197 gKillMgr.AddCount(pSpawn); 198 } 199 } 200 201 } 202 203 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite) 204 { 205 aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); 206 aiThinkTarget(pSprite, pXSprite); 207 } 208 209 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite) 210 { 211 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 212 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 213 int dx = pXSprite->targetX-pSprite->x; 214 int dy = pXSprite->targetY-pSprite->y; 215 int nAngle = getangle(dx, dy); 216 int nDist = approxDist(dx, dy); 217 aiChooseDirection(pSprite, pXSprite, nAngle); 218 if (nDist < 512 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery) 219 aiNewState(pSprite, pXSprite, &spidSearch); 220 aiThinkTarget(pSprite, pXSprite); 221 } 222 223 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite) 224 { 225 if (pXSprite->target == -1) 226 { 227 aiNewState(pSprite, pXSprite, &spidGoto); 228 return; 229 } 230 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 231 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 232 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 233 spritetype *pTarget = &sprite[pXSprite->target]; 234 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 235 int dx = pTarget->x-pSprite->x; 236 int dy = pTarget->y-pSprite->y; 237 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 238 if (pXTarget->health == 0) 239 { 240 aiNewState(pSprite, pXSprite, &spidSearch); 241 return; 242 } 243 if (IsPlayerSprite(pTarget) && powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0) 244 { 245 aiNewState(pSprite, pXSprite, &spidSearch); 246 return; 247 } 248 int nDist = approxDist(dx, dy); 249 if (nDist <= pDudeInfo->seeDist) { 250 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 251 int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2; 252 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) { 253 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) { 254 aiSetTarget(pXSprite, pXSprite->target); 255 256 switch (pSprite->type) { 257 case kDudeSpiderRed: 258 if (nDist < 0x399 && klabs(nDeltaAngle) < 85) 259 aiNewState(pSprite, pXSprite, &spidBite); 260 break; 261 case kDudeSpiderBrown: 262 case kDudeSpiderBlack: 263 if (nDist < 0x733 && nDist > 0x399 && klabs(nDeltaAngle) < 85) 264 aiNewState(pSprite, pXSprite, &spidJump); 265 else if (nDist < 0x399 && klabs(nDeltaAngle) < 85) 266 aiNewState(pSprite, pXSprite, &spidBite); 267 break; 268 case kDudeSpiderMother: 269 if (nDist < 0x733 && nDist > 0x399 && klabs(nDeltaAngle) < 85) 270 aiNewState(pSprite, pXSprite, &spidJump); 271 else if (Chance(0x8000)) 272 aiNewState(pSprite, pXSprite, &spidBirth); 273 break; 274 } 275 276 return; 277 } 278 } 279 } 280 281 aiNewState(pSprite, pXSprite, &spidGoto); 282 pXSprite->target = -1; 283 }