diff --git a/source/games/exhumed/src/aistuff.h b/source/games/exhumed/src/aistuff.h index e00685b368c..44fa34e17c1 100644 --- a/source/games/exhumed/src/aistuff.h +++ b/source/games/exhumed/src/aistuff.h @@ -645,6 +645,14 @@ struct AISnake : public ExhumedAI void Draw(RunListEvent* ev) override; }; +struct AISpider : public ExhumedAI +{ + void Tick(RunListEvent* ev) override; + void Damage(RunListEvent* ev) override; + void Draw(RunListEvent* ev) override; + void RadialDamage(RunListEvent* ev) override; +}; + void runlist_DispatchEvent(ExhumedAI* ai, int nObject, int nMessage, int nDamage, int nRun); diff --git a/source/games/exhumed/src/spider.cpp b/source/games/exhumed/src/spider.cpp index 00a49664adf..f634987f74b 100644 --- a/source/games/exhumed/src/spider.cpp +++ b/source/games/exhumed/src/spider.cpp @@ -133,327 +133,333 @@ int BuildSpider(int nSprite, int x, int y, int z, short nSector, int nAngle) return nSprite; } -void FuncSpider(int nObject, int nMessage, int nDamage, int nRun) +void AISpider::Tick(RunListEvent* ev) { - int nSpider = RunData[nRun].nVal; + int nSpider = RunData[ev->nRun].nVal; auto spp = &SpiderList[nSpider]; assert(nSpider >= 0 && nSpider < (int)SpiderList.Size()); - int nVel = 0; - int nSprite = spp->nSprite; auto sp = &sprite[nSprite]; short nAction = spp->nAction; - switch (nMessage) + int nVel = 6; + + if (spp->nHealth) { - default: + if (sp->cstat & 8) { - DebugOut("unknown msg %d for Spider\n", nMessage); - break; + sp->z = sector[sp->sectnum].ceilingz + GetSpriteHeight(nSprite); + } + else + { + Gravity(nSprite); } + } + + int nSeq = SeqOffsets[kSeqSpider] + SpiderSeq[nAction].a; + + sp->picnum = seq_GetSeqPicnum2(nSeq, spp->nFrame); + + seq_MoveSequence(nSprite, nSeq, spp->nFrame); + + int nFrameFlag = FrameFlag[SeqBase[nSeq] + spp->nFrame]; - case 0x20000: + spp->nFrame++; + if (spp->nFrame >= SeqSize[nSeq]) { + spp->nFrame = 0; + } + + short nTarget = spp->nTarget; + + if (nTarget <= -1 || sprite[nTarget].cstat & 0x101) + { + switch (nAction) { - nVel = 6; + default: + return; - if (spp->nHealth) + case 0: + { + if ((nSpider & 0x1F) == (totalmoves & 0x1F)) { - if (sp->cstat & 8) - { - sp->z = sector[sp->sectnum].ceilingz + GetSpriteHeight(nSprite); + if (nTarget < 0) { + nTarget = FindPlayer(nSprite, 100); } - else + + if (nTarget >= 0) { - Gravity(nSprite); + spp->nAction = 1; + spp->nFrame = 0; + spp->nTarget = nTarget; + + sp->xvel = bcos(sp->ang); + sp->yvel = bsin(sp->ang); + return; } } - int nSeq = SeqOffsets[kSeqSpider] + SpiderSeq[nAction].a; - - sp->picnum = seq_GetSeqPicnum2(nSeq, spp->nFrame); - - seq_MoveSequence(nSprite, nSeq, spp->nFrame); - - int nFrameFlag = FrameFlag[SeqBase[nSeq] + spp->nFrame]; - - spp->nFrame++; - if (spp->nFrame >= SeqSize[nSeq]) { + break; + } + case 1: + { + if (nTarget >= 0) { + nVel++; + } + goto case_3; + break; + } + case 4: + { + if (!spp->nFrame) + { spp->nFrame = 0; + spp->nAction = 1; } + fallthrough__; + } + case 3: + { + case_3: + short nSector = sp->sectnum; - short nTarget = spp->nTarget; - - if (nTarget <= -1 || sprite[nTarget].cstat & 0x101) + if (sp->cstat & 8) { - switch (nAction) - { - default: - return; + sp->zvel = 0; + sp->z = sector[nSector].ceilingz + (tileHeight(sp->picnum) << 5); - case 0: + if (sector[nSector].ceilingstat & 1) { - if ((nSpider & 0x1F) == (totalmoves & 0x1F)) - { - if (nTarget < 0) { - nTarget = FindPlayer(nSprite, 100); - } - - if (nTarget >= 0) - { - spp->nAction = 1; - spp->nFrame = 0; - spp->nTarget = nTarget; - - sp->xvel = bcos(sp->ang); - sp->yvel = bsin(sp->ang); - return; - } - } + sp->cstat ^= 8; + sp->zvel = 1; - break; + spp->nAction = 3; + spp->nFrame = 0; } - case 1: + } + + if ((totalmoves & 0x1F) == (nSpider & 0x1F)) + { + PlotCourseToSprite(nSprite, nTarget); + + if (RandomSize(3)) { - if (nTarget >= 0) { - nVel++; - } - goto case_3; - break; + sp->xvel = bcos(sp->ang); + sp->yvel = bsin(sp->ang); } - case 4: + else { - if (!spp->nFrame) - { - spp->nFrame = 0; - spp->nAction = 1; - } - fallthrough__; + sp->xvel = 0; + sp->yvel = 0; } - case 3: - { - case_3: - short nSector = sp->sectnum; + if (spp->nAction == 1 && RandomBit()) + { if (sp->cstat & 8) { - sp->zvel = 0; - sp->z = sector[nSector].ceilingz + (tileHeight(sp->picnum) << 5); - - if (sector[nSector].ceilingstat & 1) - { - sp->cstat ^= 8; - sp->zvel = 1; - - spp->nAction = 3; - spp->nFrame = 0; - } - } - - if ((totalmoves & 0x1F) == (nSpider & 0x1F)) - { - PlotCourseToSprite(nSprite, nTarget); - - if (RandomSize(3)) - { - sp->xvel = bcos(sp->ang); - sp->yvel = bsin(sp->ang); - } - else - { - sp->xvel = 0; - sp->yvel = 0; - } - - if (spp->nAction == 1 && RandomBit()) - { - if (sp->cstat & 8) - { - sp->cstat ^= 8; - sp->zvel = 1; - sp->z = sector[nSector].ceilingz + GetSpriteHeight(nSprite); - } - else - { - sp->zvel = -5120; - } - - spp->nAction = 3; - spp->nFrame = 0; - - if (!RandomSize(3)) { - D3PlayFX(StaticSound[kSound29], nSprite); - } - } + sp->cstat ^= 8; + sp->zvel = 1; + sp->z = sector[nSector].ceilingz + GetSpriteHeight(nSprite); } - break; - } - case 5: - { - if (!spp->nFrame) + else { - runlist_DoSubRunRec(sp->owner); - runlist_FreeRun(sp->lotag - 1); - runlist_SubRunRec(spp->nRun); - sp->cstat = 0x8000; - mydeletesprite(nSprite); + sp->zvel = -5120; } - return; - } - case 2: - { - if (nTarget != -1) - { - if (nFrameFlag & 0x80) - { - runlist_DamageEnemy(nTarget, nSprite, 3); - D3PlayFX(StaticSound[kSound38], nSprite); - } - if (PlotCourseToSprite(nSprite, nTarget) < 1024) { - return; - } + spp->nAction = 3; + spp->nFrame = 0; - spp->nAction = 1; - } - else - { - spp->nAction = 0; - sp->xvel = 0; - sp->yvel = 0; + if (!RandomSize(3)) { + D3PlayFX(StaticSound[kSound29], nSprite); } - - spp->nFrame = 0; - break; } + } + break; + } + case 5: + { + if (!spp->nFrame) + { + runlist_DoSubRunRec(sp->owner); + runlist_FreeRun(sp->lotag - 1); + runlist_SubRunRec(spp->nRun); + sp->cstat = 0x8000; + mydeletesprite(nSprite); + } + return; + } + case 2: + { + if (nTarget != -1) + { + if (nFrameFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 3); + D3PlayFX(StaticSound[kSound38], nSprite); + } + + if (PlotCourseToSprite(nSprite, nTarget) < 1024) { + return; } + + spp->nAction = 1; } else { - spp->nTarget = -1; spp->nAction = 0; - spp->nFrame = 0; - sp->xvel = 0; sp->yvel = 0; } - int nMov = movesprite(nSprite, sp->xvel << nVel, sp->yvel << nVel, sp->zvel, 1280, -1280, CLIPMASK0); + spp->nFrame = 0; + break; + } + } + } + else + { + spp->nTarget = -1; + spp->nAction = 0; + spp->nFrame = 0; - if (!nMov) - return; + sp->xvel = 0; + sp->yvel = 0; + } - if (nMov & 0x10000 - && sp->zvel < 0 - && (hihit & 0xC000) != 0xC000 - && !((sector[sp->sectnum].ceilingstat) & 1)) - { - sp->cstat |= 8; - sp->z = GetSpriteHeight(nSprite) + sector[sp->sectnum].ceilingz; - sp->zvel = 0; + int nMov = movesprite(nSprite, sp->xvel << nVel, sp->yvel << nVel, sp->zvel, 1280, -1280, CLIPMASK0); - spp->nAction = 1; - spp->nFrame = 0; - return; - } - else - { - switch (nMov & 0xC000) - { - case 0x8000: - { - sp->ang = (sp->ang + 256) & 0x7EF; - sp->xvel = bcos(sp->ang); - sp->yvel = bsin(sp->ang); - return; - } - case 0xC000: - { - if ((nMov & 0x3FFF) == nTarget) - { - int nAng = getangle(sprite[nTarget].x - sp->x, sprite[nTarget].y - sp->y); - if (AngleDiff(sp->ang, nAng) < 64) - { - spp->nAction = 2; - spp->nFrame = 0; - } - } - return; - } - default: - break; - } + if (!nMov) + return; - if (spp->nAction == 3) + if (nMov & 0x10000 + && sp->zvel < 0 + && (hihit & 0xC000) != 0xC000 + && !((sector[sp->sectnum].ceilingstat) & 1)) + { + sp->cstat |= 8; + sp->z = GetSpriteHeight(nSprite) + sector[sp->sectnum].ceilingz; + sp->zvel = 0; + + spp->nAction = 1; + spp->nFrame = 0; + return; + } + else + { + switch (nMov & 0xC000) + { + case 0x8000: + { + sp->ang = (sp->ang + 256) & 0x7EF; + sp->xvel = bcos(sp->ang); + sp->yvel = bsin(sp->ang); + return; + } + case 0xC000: + { + if ((nMov & 0x3FFF) == nTarget) + { + int nAng = getangle(sprite[nTarget].x - sp->x, sprite[nTarget].y - sp->y); + if (AngleDiff(sp->ang, nAng) < 64) { - spp->nAction = 1; + spp->nAction = 2; spp->nFrame = 0; } - return; } - return; } - - case 0x90000: - { - seq_PlotSequence(nObject, SeqOffsets[kSeqSpider] + SpiderSeq[nAction].a, spp->nFrame, SpiderSeq[nAction].b); + default: break; } - case 0xA0000: + if (spp->nAction == 3) { - if (spp->nHealth <= 0) - return; - - nDamage = runlist_CheckRadialDamage(nSprite); - // fall through - fallthrough__; + spp->nAction = 1; + spp->nFrame = 0; } + return; + } - case 0x80000: - { - if (!nDamage) - return; + return; +} - short nTarget = nObject; +void AISpider::Draw(RunListEvent* ev) +{ + int nSpider = RunData[ev->nRun].nVal; + auto spp = &SpiderList[nSpider]; + assert(nSpider >= 0 && nSpider < (int)SpiderList.Size()); - spp->nHealth -= dmgAdjust(nDamage); - if (spp->nHealth > 0) - { - /* - NOTE: - nTarget check was added, but should we return if it's invalid instead - or should code below (action set, b set) happen? - Other AI doesn't show consistency in this regard (see Scorpion code) - */ - if (nTarget > -1 && sprite[nTarget].statnum == 100) - { - spp->nTarget = nTarget; - } + short nAction = spp->nAction; - spp->nAction = 4; - spp->nFrame = 0; - } - else - { - // creature is dead, make some chunks - spp->nHealth = 0; - spp->nAction = 5; - spp->nFrame = 0; + seq_PlotSequence(ev->nIndex, SeqOffsets[kSeqSpider] + SpiderSeq[nAction].a, spp->nFrame, SpiderSeq[nAction].b); +} - sp->cstat &= 0xFEFE; +void AISpider::RadialDamage(RunListEvent* ev) +{ + int nSpider = RunData[ev->nRun].nVal; + assert(nSpider >= 0 && nSpider < (int)SpiderList.Size()); + auto spp = &SpiderList[nSpider]; - nCreaturesKilled++; + if (spp->nHealth <= 0) + return; - for (int i = 0; i < 7; i++) - { - BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqSpider, i + 41, 0)); - } - } + ev->nDamage = runlist_CheckRadialDamage(spp->nSprite); + Damage(ev); +} - return; +void AISpider::Damage(RunListEvent* ev) +{ + int nSpider = RunData[ev->nRun].nVal; + assert(nSpider >= 0 && nSpider < (int)SpiderList.Size()); + auto spp = &SpiderList[nSpider]; + int nSprite = spp->nSprite; + auto sp = &sprite[nSprite]; + + if (!ev->nDamage) + return; + + short nTarget = ev->nIndex; + + spp->nHealth -= dmgAdjust(ev->nDamage); + if (spp->nHealth > 0) + { + /* + NOTE: + nTarget check was added, but should we return if it's invalid instead + or should code below (action set, b set) happen? + Other AI doesn't show consistency in this regard (see Scorpion code) + */ + if (nTarget > -1 && sprite[nTarget].statnum == 100) + { + spp->nTarget = nTarget; } + + spp->nAction = 4; + spp->nFrame = 0; + } + else + { + // creature is dead, make some chunks + spp->nHealth = 0; + spp->nAction = 5; + spp->nFrame = 0; + + sp->cstat &= 0xFEFE; + + nCreaturesKilled++; + + for (int i = 0; i < 7; i++) + { + BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqSpider, i + 41, 0)); } + } + + return; +} + +void FuncSpider(int nObject, int nMessage, int nDamage, int nRun) +{ + AISpider ai; + runlist_DispatchEvent(&ai, nObject, nMessage, nDamage, nRun); } END_PS_NS