aizomba.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 "aizomba.h" 32 #include "blood.h" 33 #include "db.h" 34 #include "dude.h" 35 #include "eventq.h" 36 #include "levels.h" 37 #include "player.h" 38 #include "seq.h" 39 #include "sfx.h" 40 #include "trig.h" 41 42 static void HackSeqCallback(int, int); 43 static void StandSeqCallback(int, int); 44 static void thinkSearch(spritetype *, XSPRITE *); 45 static void thinkGoto(spritetype *, XSPRITE *); 46 static void thinkChase(spritetype *, XSPRITE *); 47 static void thinkPonder(spritetype *, XSPRITE *); 48 static void myThinkTarget(spritetype *, XSPRITE *); 49 static void myThinkSearch(spritetype *, XSPRITE *); 50 static void entryEZombie(spritetype *, XSPRITE *); 51 static void entryAIdle(spritetype *, XSPRITE *); 52 static void entryEStand(spritetype *, XSPRITE *); 53 54 static int nHackClient = seqRegisterClient(HackSeqCallback); 55 static int nStandClient = seqRegisterClient(StandSeqCallback); 56 57 AISTATE zombieAIdle = { kAiStateIdle, 0, -1, 0, entryAIdle, NULL, aiThinkTarget, NULL }; 58 AISTATE zombieAChase = { kAiStateChase, 8, -1, 0, NULL, aiMoveForward, thinkChase, NULL }; 59 AISTATE zombieAPonder = { kAiStateOther, 0, -1, 0, NULL, aiMoveTurn, thinkPonder, NULL }; 60 AISTATE zombieAGoto = { kAiStateMove, 8, -1, 1800, NULL, aiMoveForward, thinkGoto, &zombieAIdle }; 61 AISTATE zombieAHack = { kAiStateChase, 6, nHackClient, 80, NULL, NULL, NULL, &zombieAPonder }; 62 AISTATE zombieASearch = { kAiStateSearch, 8, -1, 1800, NULL, aiMoveForward, thinkSearch, &zombieAIdle }; 63 AISTATE zombieARecoil = { kAiStateRecoil, 5, -1, 0, NULL, NULL, NULL, &zombieAPonder }; 64 AISTATE zombieATeslaRecoil = { kAiStateRecoil, 4, -1, 0, NULL, NULL, NULL, &zombieAPonder }; 65 AISTATE zombieARecoil2 = { kAiStateRecoil, 1, -1, 360, NULL, NULL, NULL, &zombieAStand }; 66 AISTATE zombieAStand = { kAiStateMove, 11, nStandClient, 0, NULL, NULL, NULL, &zombieAPonder }; 67 AISTATE zombieEIdle = { kAiStateIdle, 12, -1, 0, NULL, NULL, aiThinkTarget, NULL }; 68 AISTATE zombieEUp2 = { kAiStateMove, 0, -1, 1, entryEZombie, NULL, NULL, &zombieASearch }; 69 AISTATE zombieEUp = { kAiStateMove, 9, -1, 180, entryEStand, NULL, NULL, &zombieEUp2 }; 70 AISTATE zombie2Idle = { kAiStateIdle, 0, -1, 0, entryAIdle, NULL, myThinkTarget, NULL }; 71 AISTATE zombie2Search = { kAiStateSearch, 8, -1, 1800, NULL, aiMoveForward, myThinkSearch, &zombie2Idle }; 72 AISTATE zombieSIdle = { kAiStateIdle, 10, -1, 0, NULL, NULL, aiThinkTarget, NULL }; 73 AISTATE zombie13AC2C = { kAiStateOther, 11, nStandClient, 0, entryEZombie, NULL, NULL, &zombieAPonder }; 74 75 static void HackSeqCallback(int, int nXSprite) 76 { 77 XSPRITE *pXSprite = &xsprite[nXSprite]; 78 int nSprite = pXSprite->reference; 79 spritetype *pSprite = &sprite[nSprite]; 80 spritetype *pTarget = &sprite[pXSprite->target]; 81 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 82 DUDEINFO *pDudeInfoT = getDudeInfo(pTarget->type); 83 int tx = pXSprite->targetX-pSprite->x; 84 int ty = pXSprite->targetY-pSprite->y; 85 int UNUSED(nDist) = approxDist(tx, ty); 86 int nAngle = getangle(tx, ty); 87 int height = (pSprite->yrepeat*pDudeInfo->eyeHeight)<<2; 88 int height2 = (pTarget->yrepeat*pDudeInfoT->eyeHeight)<<2; 89 int dz = height-height2; 90 int dx = Cos(nAngle)>>16; 91 int dy = Sin(nAngle)>>16; 92 sfxPlay3DSound(pSprite, 1101, 1, 0); 93 actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorAxe); 94 } 95 96 static void StandSeqCallback(int, int nXSprite) 97 { 98 XSPRITE *pXSprite = &xsprite[nXSprite]; 99 int nSprite = pXSprite->reference; 100 sfxPlay3DSound(&sprite[nSprite], 1102, -1, 0); 101 } 102 103 static void thinkSearch(spritetype *pSprite, XSPRITE *pXSprite) 104 { 105 aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); 106 aiLookForTarget(pSprite, pXSprite); 107 } 108 109 static void thinkGoto(spritetype *pSprite, XSPRITE *pXSprite) 110 { 111 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 112 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 113 int dx = pXSprite->targetX-pSprite->x; 114 int dy = pXSprite->targetY-pSprite->y; 115 int nAngle = getangle(dx, dy); 116 int nDist = approxDist(dx, dy); 117 aiChooseDirection(pSprite, pXSprite, nAngle); 118 if (nDist < 921 && klabs(pSprite->ang - nAngle) < pDudeInfo->periphery) 119 aiNewState(pSprite, pXSprite, &zombieASearch); 120 aiThinkTarget(pSprite, pXSprite); 121 } 122 123 static void thinkChase(spritetype *pSprite, XSPRITE *pXSprite) 124 { 125 if (pXSprite->target == -1) 126 { 127 aiNewState(pSprite, pXSprite, &zombieASearch); 128 return; 129 } 130 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 131 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 132 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 133 spritetype *pTarget = &sprite[pXSprite->target]; 134 XSPRITE *pXTarget = &xsprite[pTarget->extra]; 135 int dx = pTarget->x-pSprite->x; 136 int dy = pTarget->y-pSprite->y; 137 aiChooseDirection(pSprite, pXSprite, getangle(dx, dy)); 138 if (pXTarget->health == 0) 139 { 140 aiNewState(pSprite, pXSprite, &zombieASearch); 141 return; 142 } 143 if (IsPlayerSprite(pTarget) && (powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0 || powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpDeathMaskUseless) > 0)) 144 { 145 aiNewState(pSprite, pXSprite, &zombieAGoto); 146 return; 147 } 148 int nDist = approxDist(dx, dy); 149 if (nDist <= pDudeInfo->seeDist) 150 { 151 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 152 int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2; 153 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 154 { 155 if (klabs(nDeltaAngle) <= pDudeInfo->periphery) 156 { 157 aiSetTarget(pXSprite, pXSprite->target); 158 if (nDist < 0x400 && klabs(nDeltaAngle) < 85) 159 aiNewState(pSprite, pXSprite, &zombieAHack); 160 return; 161 } 162 } 163 } 164 165 aiNewState(pSprite, pXSprite, &zombieAGoto); 166 pXSprite->target = -1; 167 } 168 169 static void thinkPonder(spritetype *pSprite, XSPRITE *pXSprite) 170 { 171 if (pXSprite->target == -1) 172 { 173 aiNewState(pSprite, pXSprite, &zombieASearch); 174 return; 175 } 176 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 177 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 178 dassert(pXSprite->target >= 0 && pXSprite->target < kMaxSprites); 179 spritetype *pTarget = &sprite[pXSprite->target]; 180 if (!VanillaMode() && !xspriRangeIsFine(pTarget->extra)) 181 { 182 aiNewState(pSprite, pXSprite, &zombieASearch); 183 return; 184 } 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, &zombieASearch); 192 return; 193 } 194 if (IsPlayerSprite(pTarget) && (powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpShadowCloak) > 0 || powerupCheck(&gPlayer[pTarget->type-kDudePlayer1], kPwUpDeathMaskUseless) > 0)) 195 { 196 aiNewState(pSprite, pXSprite, &zombieAGoto); 197 return; 198 } 199 int nDist = approxDist(dx, dy); 200 if (nDist <= pDudeInfo->seeDist) 201 { 202 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 203 int height = (pDudeInfo->eyeHeight*pSprite->yrepeat)<<2; 204 if (cansee(pTarget->x, pTarget->y, pTarget->z, pTarget->sectnum, pSprite->x, pSprite->y, pSprite->z - height, pSprite->sectnum)) 205 { 206 if (klabs(nDeltaAngle) <= pDudeInfo->periphery) 207 { 208 aiSetTarget(pXSprite, pXSprite->target); 209 if (nDist < 0x400) 210 { 211 if (klabs(nDeltaAngle) < 85) 212 { 213 sfxPlay3DSound(pSprite, 1101, 1, 0); 214 aiNewState(pSprite, pXSprite, &zombieAHack); 215 } 216 return; 217 } 218 } 219 } 220 } 221 222 aiNewState(pSprite, pXSprite, &zombieAChase); 223 } 224 225 static void myThinkTarget(spritetype *pSprite, XSPRITE *pXSprite) 226 { 227 dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax); 228 DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type); 229 int nOwner = spriRangeIsFine(pSprite->owner) ? pSprite->owner : -1; 230 for (int p = connecthead; p >= 0; p = connectpoint2[p]) 231 { 232 PLAYER *pPlayer = &gPlayer[p]; 233 if (nOwner == pPlayer->nSprite || pPlayer->pXSprite->health == 0 || powerupCheck(pPlayer, kPwUpShadowCloak) > 0) 234 continue; 235 int x = pPlayer->pSprite->x; 236 int y = pPlayer->pSprite->y; 237 int z = pPlayer->pSprite->z; 238 int nSector = pPlayer->pSprite->sectnum; 239 int dx = x-pSprite->x; 240 int dy = y-pSprite->y; 241 int nDist = approxDist(dx, dy); 242 if (nDist > pDudeInfo->seeDist && nDist > pDudeInfo->hearDist) 243 continue; 244 if (!cansee(x, y, z, nSector, pSprite->x, pSprite->y, pSprite->z-((pDudeInfo->eyeHeight*pSprite->yrepeat)<<2), pSprite->sectnum)) 245 continue; 246 int nDeltaAngle = ((getangle(dx,dy)+1024-pSprite->ang)&2047)-1024; 247 if (nDist < pDudeInfo->seeDist && klabs(nDeltaAngle) <= pDudeInfo->periphery) 248 { 249 aiSetTarget(pXSprite, pPlayer->nSprite); 250 aiActivateDude(pSprite, pXSprite); 251 } 252 else if (nDist < pDudeInfo->hearDist) 253 { 254 aiSetTarget(pXSprite, x, y, z); 255 aiActivateDude(pSprite, pXSprite); 256 } 257 else 258 continue; 259 break; 260 } 261 } 262 263 static void myThinkSearch(spritetype *pSprite, XSPRITE *pXSprite) 264 { 265 aiChooseDirection(pSprite, pXSprite, pXSprite->goalAng); 266 myThinkTarget(pSprite, pXSprite); 267 } 268 269 static void entryEZombie(spritetype *pSprite, XSPRITE *pXSprite) 270 { 271 UNREFERENCED_PARAMETER(pXSprite); 272 pSprite->type = kDudeZombieAxeNormal; 273 pSprite->flags |= 1; 274 } 275 276 static void entryAIdle(spritetype *pSprite, XSPRITE *pXSprite) 277 { 278 UNREFERENCED_PARAMETER(pSprite); 279 pXSprite->target = -1; 280 } 281 282 static void entryEStand(spritetype *pSprite, XSPRITE *pXSprite) 283 { 284 sfxPlay3DSound(pSprite, 1100, -1, 0); 285 pSprite->ang = getangle(pXSprite->targetX-pSprite->x, pXSprite->targetY-pSprite->y); 286 }