diff --git a/source/games/exhumed/src/aistuff.h b/source/games/exhumed/src/aistuff.h index 332279694..ff508e8e7 100644 --- a/source/games/exhumed/src/aistuff.h +++ b/source/games/exhumed/src/aistuff.h @@ -563,6 +563,30 @@ struct AIPlayer : public ExhumedAI void RadialDamage(RunListEvent* ev) override; }; +struct AIQueenEgg : public ExhumedAI +{ + void Tick(RunListEvent* ev) override; + void Damage(RunListEvent* ev) override; + void Draw(RunListEvent* ev) override; + void RadialDamage(RunListEvent* ev) override; +}; + +struct AIQueenHead : public ExhumedAI +{ + void Tick(RunListEvent* ev) override; + void Damage(RunListEvent* ev) override; + void Draw(RunListEvent* ev) override; + void RadialDamage(RunListEvent* ev) override; +}; + +struct AIQueen : 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); typedef void(*AiFunc)(int, int, int, int nRun); diff --git a/source/games/exhumed/src/queen.cpp b/source/games/exhumed/src/queen.cpp index ce2d2ec25..22760de59 100644 --- a/source/games/exhumed/src/queen.cpp +++ b/source/games/exhumed/src/queen.cpp @@ -29,9 +29,9 @@ BEGIN_PS_NS enum { - kMaxQueens = 1, - kMaxEggs = 10, - kMaxTails = 7 + kMaxQueens = 1, + kMaxEggs = 10, + kMaxTails = 7 }; short QueenCount = 0; @@ -236,7 +236,7 @@ void BlowChunks(int nSprite) void DestroyEgg(short nEgg) { short nSprite = QueenEgg[nEgg].nSprite; - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; if (QueenEgg[nEgg].nAction != 4) { @@ -273,7 +273,7 @@ void DestroyAllEggs() void SetHeadVel(short nSprite) { - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; short nAngle = pSprite->ang; pSprite->xvel = bcos(nAngle, nVelShift); @@ -284,7 +284,7 @@ int QueenAngleChase(short nSprite, short nSprite2, int val1, int val2) { short nAngle; - spritetype *pSprite = &sprite[nSprite]; + spritetype* pSprite = &sprite[nSprite]; if (nSprite2 < 0) { pSprite->zvel = 0; @@ -292,7 +292,7 @@ int QueenAngleChase(short nSprite, short nSprite2, int val1, int val2) } else { - spritetype *pSprite2 = &sprite[nSprite2]; + spritetype* pSprite2 = &sprite[nSprite2]; int nTileY = (tileHeight(pSprite2->picnum) * pSprite2->yrepeat) * 2; int nMyAngle = GetMyAngle(pSprite2->x - pSprite->x, pSprite2->y - pSprite->y); @@ -318,7 +318,7 @@ int QueenAngleChase(short nSprite, short nSprite2, int val1, int val2) if (abs(nAngDelta) > 127) { - val1 /= abs(nAngDelta>>7); + val1 /= abs(nAngDelta >> 7); if (val1 < 256) val1 = 256; } @@ -384,7 +384,7 @@ int DestroyTailPart() void BuildTail() { short nSprite = QueenHead.nSprite; - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; int x = pSprite->x; int y = pSprite->x; @@ -442,7 +442,7 @@ void BuildQueenEgg(short nQueen, int nVal) } short nSprite = QueenList[nQueen].nSprite; - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; int x = pSprite->x; int y = pSprite->y; @@ -452,7 +452,7 @@ void BuildQueenEgg(short nQueen, int nVal) int nSprite2 = insertsprite(nSector, 121); assert(nSprite2 >= 0 && nSprite2 < kMaxSprites); - auto pSprite2 = &sprite[nSprite2]; + auto pSprite2 = &sprite[nSprite2]; pSprite2->x = x; pSprite2->y = y; @@ -509,205 +509,205 @@ void BuildQueenEgg(short nQueen, int nVal) QueenEgg[nEgg].nRunPtr = runlist_AddRunRec(NewRun, nEgg, 0x1D0000); } -void FuncQueenEgg(int nObject, int nMessage, int nDamage, int nRun) +void AIQueenEgg::Tick(RunListEvent* ev) { - short nEgg = RunData[nRun].nVal; - - Egg *pEgg = &QueenEgg[nEgg]; + short nEgg = RunData[ev->nRun].nVal; + Egg* pEgg = &QueenEgg[nEgg]; short nSprite = pEgg->nSprite; - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; short nAction = pEgg->nAction; short nTarget; bool bVal = false; - switch (nMessage) + if (pEgg->nHealth <= 0) { - default: + DestroyEgg(nEgg); + return; + } + + if (nAction == 0 || nAction == 4) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqQueenEgg] + EggSeq[nAction].a; + + pSprite->picnum = seq_GetSeqPicnum2(nSeq, pEgg->nFrame); + + if (nAction != 4) + { + seq_MoveSequence(nSprite, nSeq, pEgg->nFrame); + + pEgg->nFrame++; + if (pEgg->nFrame >= SeqSize[nSeq]) { - DebugOut("unknown msg %d for Queenhead\n", nMessage); + pEgg->nFrame = 0; + bVal = true; + } + + nTarget = UpdateEnemy(&pEgg->nTarget); + pEgg->nTarget = nTarget; + + if (nTarget >= 0 && (sprite[nTarget].cstat & 0x101) == 0) + { + pEgg->nTarget = -1; + pEgg->nAction = 0; + } + else + { + nTarget = FindPlayer(-nSprite, 1000); + pEgg->nTarget = nTarget; + } + } + + switch (nAction) + { + case 0: + { + int nMov = MoveCreature(nSprite); + if (!nMov) { break; } - case 0x20000: + if (nMov & 0x20000) { - if (pEgg->nHealth <= 0) + if (!RandomSize(1)) + { + pEgg->nAction = 1; + pEgg->nFrame = 0; + } + else { DestroyEgg(nEgg); + } + } + else + { + short nAngle; + + switch (nMov & 0xC000) + { + default: return; + case 0x8000: + nAngle = GetWallNormal(nMov & 0x3FFF); + break; + case 0xC000: + nAngle = sprite[nMov & 0x3FFF].ang; + break; } - if (nAction == 0 || nAction == 4) { - Gravity(nSprite); - } - - short nSeq = SeqOffsets[kSeqQueenEgg] + EggSeq[nAction].a; - - pSprite->picnum = seq_GetSeqPicnum2(nSeq, pEgg->nFrame); - - if (nAction != 4) - { - seq_MoveSequence(nSprite, nSeq, pEgg->nFrame); - - pEgg->nFrame++; - if (pEgg->nFrame >= SeqSize[nSeq]) - { - pEgg->nFrame = 0; - bVal = true; - } - - nTarget = UpdateEnemy(&pEgg->nTarget); - pEgg->nTarget = nTarget; - - if (nTarget >= 0 && (sprite[nTarget].cstat & 0x101) == 0) - { - pEgg->nTarget = -1; - pEgg->nAction = 0; - } - else - { - nTarget = FindPlayer(-nSprite, 1000); - pEgg->nTarget = nTarget; - } - } - - switch (nAction) - { - case 0: - { - int nMov = MoveCreature(nSprite); - if (!nMov) { - break; - } - - if (nMov & 0x20000) - { - if (!RandomSize(1)) - { - pEgg->nAction = 1; - pEgg->nFrame = 0; - } - else - { - DestroyEgg(nEgg); - } - } - else - { - short nAngle; - - switch (nMov & 0xC000) - { - default: - return; - case 0x8000: - nAngle = GetWallNormal(nMov & 0x3FFF); - break; - case 0xC000: - nAngle = sprite[nMov & 0x3FFF].ang; - break; - } - - pSprite->ang = nAngle; - pSprite->xvel = bcos(nAngle, -1); - pSprite->yvel = bsin(nAngle, -1); - } - - break; - } - - case 1: - { - if (bVal) - { - pEgg->nAction = 3; - pSprite->cstat = 0x101; - } - break; - } - - case 2: - case 3: - { - int nMov = QueenAngleChase(nSprite, nTarget, nHeadVel, 64); - - switch (nMov & 0xC000) - { - case 0xC000: - if (sprite[nMov & 0x3FFF].statnum != 121) - { - runlist_DamageEnemy(nMov & 0x3FFF, nSprite, 5); - } - fallthrough__; - case 0x8000: - pSprite->ang += (RandomSize(9) + 768); - pSprite->ang &= kAngleMask; - pSprite->xvel = bcos(pSprite->ang, -3); - pSprite->yvel = bsin(pSprite->ang, -3); - pSprite->zvel = -RandomSize(5); - break; - } - - return; - } - - case 4: - { - int nMov = MoveCreature(nSprite); - - if (nMov & 0x20000) - { - pSprite->zvel = -(pSprite->zvel - 256); - if (pSprite->zvel < -512) - { - pSprite->zvel = 0; - } - } - - pEgg->field_C--; - if (pEgg->field_C <= 0) - { - short nWaspSprite = BuildWasp(-2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, pSprite->ang); - pSprite->z = sprite[nWaspSprite].z; - - DestroyEgg(nEgg); - } - break; - } - } - break; + pSprite->ang = nAngle; + pSprite->xvel = bcos(nAngle, -1); + pSprite->yvel = bsin(nAngle, -1); } - case 0xA0000: + break; + } + + case 1: + { + if (bVal) { - if (sprite[nRadialSpr].statnum != 121 && (pSprite->cstat & 0x101) != 0) - { - nDamage = runlist_CheckRadialDamage(nSprite); - - pEgg->nHealth -= nDamage; - } - break; + pEgg->nAction = 3; + pSprite->cstat = 0x101; } + break; + } - case 0x80000: + case 2: + case 3: + { + int nMov = QueenAngleChase(nSprite, nTarget, nHeadVel, 64); + + switch (nMov & 0xC000) { - if (nDamage != 0 && pEgg->nHealth > 0) + case 0xC000: + if (sprite[nMov & 0x3FFF].statnum != 121) { - QueenEgg[nEgg].nHealth -= nDamage; - - if (QueenEgg[nEgg].nHealth <= 0) - DestroyEgg(nEgg); + runlist_DamageEnemy(nMov & 0x3FFF, nSprite, 5); } + fallthrough__; + case 0x8000: + pSprite->ang += (RandomSize(9) + 768); + pSprite->ang &= kAngleMask; + pSprite->xvel = bcos(pSprite->ang, -3); + pSprite->yvel = bsin(pSprite->ang, -3); + pSprite->zvel = -RandomSize(5); break; } - case 0x90000: + return; + } + + case 4: + { + int nMov = MoveCreature(nSprite); + + if (nMov & 0x20000) { - seq_PlotSequence(nObject, SeqOffsets[kSeqQueenEgg] + EggSeq[nAction].a, pEgg->nFrame, EggSeq[nAction].b); - break; + pSprite->zvel = -(pSprite->zvel - 256); + if (pSprite->zvel < -512) + { + pSprite->zvel = 0; + } } + + pEgg->field_C--; + if (pEgg->field_C <= 0) + { + short nWaspSprite = BuildWasp(-2, pSprite->x, pSprite->y, pSprite->z, pSprite->sectnum, pSprite->ang); + pSprite->z = sprite[nWaspSprite].z; + + DestroyEgg(nEgg); } + break; + } + } +} + +void AIQueenEgg::RadialDamage(RunListEvent* ev) +{ + short nEgg = RunData[ev->nRun].nVal; + Egg* pEgg = &QueenEgg[nEgg]; + short nSprite = pEgg->nSprite; + auto pSprite = &sprite[nSprite]; + + if (sprite[nRadialSpr].statnum != 121 && (pSprite->cstat & 0x101) != 0) + { + int nDamage = runlist_CheckRadialDamage(nSprite); + + pEgg->nHealth -= nDamage; + } +} + +void AIQueenEgg::Damage(RunListEvent* ev) +{ + short nEgg = RunData[ev->nRun].nVal; + Egg* pEgg = &QueenEgg[nEgg]; + + if (ev->nDamage != 0 && pEgg->nHealth > 0) + { + pEgg->nHealth -= ev->nDamage; + + if (pEgg->nHealth <= 0) + DestroyEgg(nEgg); + } +} + +void AIQueenEgg::Draw(RunListEvent* ev) +{ + short nEgg = RunData[ev->nRun].nVal; + Egg* pEgg = &QueenEgg[nEgg]; + seq_PlotSequence(ev->nIndex, SeqOffsets[kSeqQueenEgg] + EggSeq[pEgg->nAction].a, pEgg->nFrame, EggSeq[pEgg->nAction].b); +} + + +void FuncQueenEgg(int nObject, int nMessage, int nDamage, int nRun) +{ + AIQueenEgg ai; + runlist_DispatchEvent(&ai, nObject, nMessage, nDamage, nRun); } void BuildQueenHead(short nQueen) @@ -723,7 +723,7 @@ void BuildQueenHead(short nQueen) int nSprite2 = insertsprite(nSector, 121); assert(nSprite2 >= 0 && nSprite2 < kMaxSprites); - auto pSprite2 = &sprite[nSprite2]; + auto pSprite2 = &sprite[nSprite2]; pSprite2->x = x; pSprite2->y = y; @@ -762,381 +762,384 @@ void BuildQueenHead(short nQueen) QueenHead.tails = 0; } -void FuncQueenHead(int nObject, int nMessage, int nDamage, int nRun) +void AIQueenHead::Tick(RunListEvent* ev) { - short nHead = RunData[nRun].nVal; + short nHead = RunData[ev->nRun].nVal; short nSprite = QueenHead.nSprite; - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; int nSector = pSprite->sectnum; assert(nSector >= 0 && nSector < kMaxSectors); short nAction = QueenHead.nAction; - short nTarget, nHd; - int var_14 = 0; - switch (nMessage) + if (nAction == 0) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqQueen] + HeadSeq[QueenHead.nAction].a; + + seq_MoveSequence(nSprite, nSeq, QueenHead.nFrame); + + pSprite->picnum = seq_GetSeqPicnum2(nSeq, QueenHead.nFrame); + + QueenHead.nFrame++; + if (QueenHead.nFrame >= SeqSize[nSeq]) { - case 0x20000: + QueenHead.nFrame = 0; + var_14 = 1; + } + + nTarget = QueenHead.nTarget; + + if (nTarget > -1) + { + if (!(sprite[nTarget].cstat & 0x101)) { - if (nAction == 0) { - Gravity(nSprite); - } - - short nSeq = SeqOffsets[kSeqQueen] + HeadSeq[QueenHead.nAction].a; - - seq_MoveSequence(nSprite, nSeq, QueenHead.nFrame); - - pSprite->picnum = seq_GetSeqPicnum2(nSeq, QueenHead.nFrame); - - QueenHead.nFrame++; - if (QueenHead.nFrame >= SeqSize[nSeq]) - { - QueenHead.nFrame = 0; - var_14 = 1; - } - - nTarget = QueenHead.nTarget; - - if (nTarget > -1) - { - if (!(sprite[nTarget].cstat & 0x101)) - { - nTarget = -1; - QueenHead.nTarget = nTarget; - } - } - else - { - nTarget = FindPlayer(nSprite, 1000); - QueenHead.nTarget = nTarget; - } - - switch (nAction) - { - case 0: - if (QueenHead.field_C > 0) - { - QueenHead.field_C--; - if (QueenHead.field_C == 0) - { - BuildTail(); - - QueenHead.nAction = 6; - nHeadVel = 800; - pSprite->cstat = 0x101; - } - else if (QueenHead.field_C < 60) - { - pSprite->shade--; - } - } - else - { - int nMov = MoveCreature(nSprite); - - // original BUG - this line doesn't exist in original code? - short nNewAng = pSprite->ang; - - switch (nMov & 0xFC000) - { - default: - return; - case 0xC000: - nNewAng = sprite[nMov & 0x3FFF].ang; - break; - case 0x8000: - nNewAng = GetWallNormal(nMov & 0x3FFF); - break; - case 0x20000: - pSprite->zvel = -(pSprite->zvel >> 1); - - if (pSprite->zvel > -256) - { - nVelShift = 100; - pSprite->zvel = 0; - } - break; - } - - // original BUG - var_18 isn't being set if the check above == 0x20000 ? - pSprite->ang = nNewAng; - nVelShift++; - - if (nVelShift < 5) - { - SetHeadVel(nSprite); - } - else - { - pSprite->xvel = 0; - pSprite->yvel = 0; - - if (pSprite->zvel == 0) - { - QueenHead.field_C = 120; - } - } - } - - break; - - case 6: - if (var_14) - { - QueenHead.nAction = 1; - QueenHead.nFrame = 0; - break; - } - fallthrough__; - - case 1: - if ((sprite[nTarget].z - 51200) > pSprite->z) - { - QueenHead.nAction = 4; - QueenHead.nFrame = 0; - } - else - { - pSprite->z -= 2048; - goto __MOVEQS; - } - break; - - case 4: - case 7: - case 8: - if (var_14) - { - int nRnd = RandomSize(2); - - if (nRnd == 0) - { - QueenHead.nAction = 4; - } - else if (nRnd == 1) - { - QueenHead.nAction = 7; - } - else - { - QueenHead.nAction = 8; - } - } - - if (nTarget > -1) - { - int nMov = QueenAngleChase(nSprite, nTarget, nHeadVel, 64); - - switch (nMov & 0xC000) - { - case 0x8000: - break; - case 0xC000: - if ((nMov & 0x3FFF) == nTarget) - { - runlist_DamageEnemy(nTarget, nSprite, 10); - D3PlayFX(StaticSound[kSoundQTail] | 0x2000, nSprite); - - pSprite->ang += RandomSize(9) + 768; - pSprite->ang &= kAngleMask; - - pSprite->zvel = (-20) - RandomSize(6); - - SetHeadVel(nSprite); - } - break; - } - } - - // switch break. MoveQS stuff? -__MOVEQS: - MoveQX[nQHead] = pSprite->x; - MoveQY[nQHead] = pSprite->y; - MoveQZ[nQHead] = pSprite->z; - assert(pSprite->sectnum >= 0 && pSprite->sectnum < kMaxSectors); - MoveQS[nQHead] = pSprite->sectnum; - MoveQA[nQHead] = pSprite->ang; - - nHd = nQHead; - - for (int i = 0; i < QueenHead.tails; i++) - { - nHd -= 3; - if (nHd < 0) { - nHd += (24 + 1); // TODO - enum/define for these - //assert(nHd < 24 && nHd >= 0); - } - - int var_20 = MoveQS[nHd]; - short nTSprite = tailspr[i]; - - if (var_20 != sprite[nTSprite].sectnum) - { - assert(var_20 >= 0 && var_20 < kMaxSectors); - mychangespritesect(nTSprite, var_20); - } - - sprite[nTSprite].x = MoveQX[nHd]; - sprite[nTSprite].y = MoveQY[nHd]; - sprite[nTSprite].z = MoveQZ[nHd]; - sprite[nTSprite].ang = MoveQA[nHd]; - } - - nQHead++; - if (nQHead >= 25) - { - nQHead = 0; - } - - break; - - case 5: - QueenHead.field_C--; - if (QueenHead.field_C <= 0) - { - QueenHead.field_C = 3; - - if (QueenHead.tails--) - { - if (QueenHead.tails >= 15 || QueenHead.tails < 10) - { - int x = pSprite->x; - int y = pSprite->y; - int z = pSprite->z; - short nSector = pSprite->sectnum; - int nAngle = RandomSize(11) & kAngleMask; - - pSprite->xrepeat = 127 - QueenHead.tails; - pSprite->yrepeat = 127 - QueenHead.tails; - - pSprite->cstat = 0x8000; - - // DEMO-TODO: in disassembly angle was used without masking and thus causing OOB issue. - // This behavior probably would be needed emulated for demo compatibility - int dx = bcos(nAngle, 10); - int dy = bsin(nAngle, 10); - int dz = (RandomSize(5) - RandomSize(5)) << 7; - - movesprite(nSprite, dx, dy, dz, 0, 0, CLIPMASK1); - - BlowChunks(nSprite); - BuildExplosion(nSprite); - - mychangespritesect(nSprite, nSector); - - pSprite->x = x; - pSprite->y = y; - pSprite->z = z; - - if (QueenHead.tails < 10) { - for (int i = (10 - QueenHead.tails) * 2; i > 0; i--) - { - BuildLavaLimb(nSprite, i, GetSpriteHeight(nSprite)); - } - } - } - } - else - { - BuildExplosion(nSprite); - - int i; - - for (i = 0; i < 10; i++) - { - BlowChunks(nSprite); - } - - for (i = 0; i < 20; i++) - { - BuildLavaLimb(nSprite, i, GetSpriteHeight(nSprite)); - } - - runlist_SubRunRec(pSprite->owner); - runlist_SubRunRec(QueenHead.field_8); - mydeletesprite(nSprite); - runlist_ChangeChannel(QueenChan[0], 1); - } - } - break; - } - break; - } - - case 0xA0000: - if (sprite[nRadialSpr].statnum != 121 && (pSprite->cstat & 0x101) != 0) - { - nDamage = runlist_CheckRadialDamage(nSprite); - if (!nDamage) - break; - } - else - break; - // fall through to case 0x80000 - fallthrough__; - - case 0x80000: - if (QueenHead.nHealth > 0 && nDamage != 0) - { - QueenHead.nHealth -= nDamage; - - if (!RandomSize(4)) - { - QueenHead.nTarget = nObject; - QueenHead.nAction = 7; - QueenHead.nFrame = 0; - } - - if (QueenHead.nHealth <= 0) - { - if (DestroyTailPart()) - { - QueenHead.nHealth = 200; - nHeadVel += 100; - } - else - { - QueenHead.nAction = 5; - QueenHead.nFrame = 0; - QueenHead.field_C = 0; - QueenHead.tails = 80; - pSprite->cstat = 0; - } - } - } - break; - - case 0x90000: - { - short nSeq = SeqOffsets[kSeqQueen]; - - int edx; - - if (nHead == 0) - { - edx = HeadSeq[nAction].b; - nSeq += HeadSeq[nAction].a; - } - else - { - edx = 1; - nSeq += 73; - } - - seq_PlotSequence(nObject, nSeq, QueenHead.nFrame, edx); - break; - } - - default: - { - DebugOut("unknown msg %d for Queenhead\n", nMessage); - break; + nTarget = -1; + QueenHead.nTarget = nTarget; } } + else + { + nTarget = FindPlayer(nSprite, 1000); + QueenHead.nTarget = nTarget; + } + + switch (nAction) + { + case 0: + if (QueenHead.field_C > 0) + { + QueenHead.field_C--; + if (QueenHead.field_C == 0) + { + BuildTail(); + + QueenHead.nAction = 6; + nHeadVel = 800; + pSprite->cstat = 0x101; + } + else if (QueenHead.field_C < 60) + { + pSprite->shade--; + } + } + else + { + int nMov = MoveCreature(nSprite); + + // original BUG - this line doesn't exist in original code? + short nNewAng = pSprite->ang; + + switch (nMov & 0xFC000) + { + default: + return; + case 0xC000: + nNewAng = sprite[nMov & 0x3FFF].ang; + break; + case 0x8000: + nNewAng = GetWallNormal(nMov & 0x3FFF); + break; + case 0x20000: + pSprite->zvel = -(pSprite->zvel >> 1); + + if (pSprite->zvel > -256) + { + nVelShift = 100; + pSprite->zvel = 0; + } + break; + } + + // original BUG - var_18 isn't being set if the check above == 0x20000 ? + pSprite->ang = nNewAng; + nVelShift++; + + if (nVelShift < 5) + { + SetHeadVel(nSprite); + } + else + { + pSprite->xvel = 0; + pSprite->yvel = 0; + + if (pSprite->zvel == 0) + { + QueenHead.field_C = 120; + } + } + } + + break; + + case 6: + if (var_14) + { + QueenHead.nAction = 1; + QueenHead.nFrame = 0; + break; + } + fallthrough__; + + case 1: + if ((sprite[nTarget].z - 51200) > pSprite->z) + { + QueenHead.nAction = 4; + QueenHead.nFrame = 0; + } + else + { + pSprite->z -= 2048; + goto __MOVEQS; + } + break; + + case 4: + case 7: + case 8: + if (var_14) + { + int nRnd = RandomSize(2); + + if (nRnd == 0) + { + QueenHead.nAction = 4; + } + else if (nRnd == 1) + { + QueenHead.nAction = 7; + } + else + { + QueenHead.nAction = 8; + } + } + + if (nTarget > -1) + { + int nMov = QueenAngleChase(nSprite, nTarget, nHeadVel, 64); + + switch (nMov & 0xC000) + { + case 0x8000: + break; + case 0xC000: + if ((nMov & 0x3FFF) == nTarget) + { + runlist_DamageEnemy(nTarget, nSprite, 10); + D3PlayFX(StaticSound[kSoundQTail] | 0x2000, nSprite); + + pSprite->ang += RandomSize(9) + 768; + pSprite->ang &= kAngleMask; + + pSprite->zvel = (-20) - RandomSize(6); + + SetHeadVel(nSprite); + } + break; + } + } + + // switch break. MoveQS stuff? + __MOVEQS: + MoveQX[nQHead] = pSprite->x; + MoveQY[nQHead] = pSprite->y; + MoveQZ[nQHead] = pSprite->z; + assert(pSprite->sectnum >= 0 && pSprite->sectnum < kMaxSectors); + MoveQS[nQHead] = pSprite->sectnum; + MoveQA[nQHead] = pSprite->ang; + + nHd = nQHead; + + for (int i = 0; i < QueenHead.tails; i++) + { + nHd -= 3; + if (nHd < 0) { + nHd += (24 + 1); // TODO - enum/define for these + //assert(nHd < 24 && nHd >= 0); + } + + int var_20 = MoveQS[nHd]; + short nTSprite = tailspr[i]; + + if (var_20 != sprite[nTSprite].sectnum) + { + assert(var_20 >= 0 && var_20 < kMaxSectors); + mychangespritesect(nTSprite, var_20); + } + + sprite[nTSprite].x = MoveQX[nHd]; + sprite[nTSprite].y = MoveQY[nHd]; + sprite[nTSprite].z = MoveQZ[nHd]; + sprite[nTSprite].ang = MoveQA[nHd]; + } + + nQHead++; + if (nQHead >= 25) + { + nQHead = 0; + } + + break; + + case 5: + QueenHead.field_C--; + if (QueenHead.field_C <= 0) + { + QueenHead.field_C = 3; + + if (QueenHead.tails--) + { + if (QueenHead.tails >= 15 || QueenHead.tails < 10) + { + int x = pSprite->x; + int y = pSprite->y; + int z = pSprite->z; + short nSector = pSprite->sectnum; + int nAngle = RandomSize(11) & kAngleMask; + + pSprite->xrepeat = 127 - QueenHead.tails; + pSprite->yrepeat = 127 - QueenHead.tails; + + pSprite->cstat = 0x8000; + + // DEMO-TODO: in disassembly angle was used without masking and thus causing OOB issue. + // This behavior probably would be needed emulated for demo compatibility + int dx = bcos(nAngle, 10); + int dy = bsin(nAngle, 10); + int dz = (RandomSize(5) - RandomSize(5)) << 7; + + movesprite(nSprite, dx, dy, dz, 0, 0, CLIPMASK1); + + BlowChunks(nSprite); + BuildExplosion(nSprite); + + mychangespritesect(nSprite, nSector); + + pSprite->x = x; + pSprite->y = y; + pSprite->z = z; + + if (QueenHead.tails < 10) { + for (int i = (10 - QueenHead.tails) * 2; i > 0; i--) + { + BuildLavaLimb(nSprite, i, GetSpriteHeight(nSprite)); + } + } + } + } + else + { + BuildExplosion(nSprite); + + int i; + + for (i = 0; i < 10; i++) + { + BlowChunks(nSprite); + } + + for (i = 0; i < 20; i++) + { + BuildLavaLimb(nSprite, i, GetSpriteHeight(nSprite)); + } + + runlist_SubRunRec(pSprite->owner); + runlist_SubRunRec(QueenHead.field_8); + mydeletesprite(nSprite); + runlist_ChangeChannel(QueenChan[0], 1); + } + } + break; + } +} + +void AIQueenHead::RadialDamage(RunListEvent* ev) +{ + short nHead = RunData[ev->nRun].nVal; + + short nSprite = QueenHead.nSprite; + auto pSprite = &sprite[nSprite]; + + if (sprite[nRadialSpr].statnum != 121 && (pSprite->cstat & 0x101) != 0) + { + + ev->nDamage = runlist_CheckRadialDamage(nSprite); + if (ev->nDamage) Damage(ev); + } +} + +void AIQueenHead::Damage(RunListEvent* ev) +{ + short nHead = RunData[ev->nRun].nVal; + + short nSprite = QueenHead.nSprite; + auto pSprite = &sprite[nSprite]; + + if (QueenHead.nHealth > 0 && ev->nDamage != 0) + { + QueenHead.nHealth -= ev->nDamage; + + if (!RandomSize(4)) + { + QueenHead.nTarget = ev->nIndex; + QueenHead.nAction = 7; + QueenHead.nFrame = 0; + } + + if (QueenHead.nHealth <= 0) + { + if (DestroyTailPart()) + { + QueenHead.nHealth = 200; + nHeadVel += 100; + } + else + { + QueenHead.nAction = 5; + QueenHead.nFrame = 0; + QueenHead.field_C = 0; + QueenHead.tails = 80; + pSprite->cstat = 0; + } + } + } +} + +void AIQueenHead::Draw(RunListEvent* ev) +{ + short nHead = RunData[ev->nRun].nVal; + short nAction = QueenHead.nAction; + + short nSeq = SeqOffsets[kSeqQueen]; + + int edx; + + if (nHead == 0) + { + edx = HeadSeq[nAction].b; + nSeq += HeadSeq[nAction].a; + } + else + { + edx = 1; + nSeq += 73; + } + + seq_PlotSequence(ev->nIndex, nSeq, QueenHead.nFrame, edx); +} + + +void FuncQueenHead(int nObject, int nMessage, int nDamage, int nRun) +{ + AIQueenHead ai; + runlist_DispatchEvent(&ai, nObject, nMessage, nDamage, nRun); } void BuildQueen(int nSprite, int x, int y, int z, int nSector, int nAngle, int nChannel) @@ -1147,12 +1150,12 @@ void BuildQueen(int nSprite, int x, int y, int z, int nSector, int nAngle, int n if (nQueen < 0) { return; } - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; if (nSprite == -1) { nSprite = insertsprite(nSector, 121); - pSprite = &sprite[nSprite]; + pSprite = &sprite[nSprite]; } else @@ -1210,357 +1213,356 @@ void BuildQueen(int nSprite, int x, int y, int z, int nSector, int nAngle, int n void SetQueenSpeed(short nSprite, int nSpeed) { - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; pSprite->xvel = bcos(pSprite->ang, -(2 - nSpeed)); pSprite->yvel = bsin(pSprite->ang, -(2 - nSpeed)); } -void FuncQueen(int nObject, int nMessage, int nDamage, int nRun) +void AIQueen::Tick(RunListEvent* ev) { - short nQueen = RunData[nRun].nVal; + short nQueen = RunData[ev->nRun].nVal; assert(nQueen >= 0 && nQueen < kMaxQueens); short nSprite = QueenList[nQueen].nSprite; - auto pSprite = &sprite[nSprite]; + auto pSprite = &sprite[nSprite]; short nAction = QueenList[nQueen].nAction; short si = QueenList[nQueen].field_A; short nTarget = QueenList[nQueen].nTarget; bool bVal = false; - switch (nMessage) + if (si < 3) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqQueen] + QueenSeq[nAction].a; + + pSprite->picnum = seq_GetSeqPicnum2(nSeq, QueenList[nQueen].nFrame); + + seq_MoveSequence(nSprite, nSeq, QueenList[nQueen].nFrame); + + QueenList[nQueen].nFrame++; + if (QueenList[nQueen].nFrame >= SeqSize[nSeq]) { + QueenList[nQueen].nFrame = 0; + bVal = true; + } - default: + short nFlag = FrameFlag[SeqBase[nSeq] + QueenList[nQueen].nFrame]; + + if (nTarget > -1) + { + if (nAction < 7) { - DebugOut("unknown msg %d for Queen\n", nMessage); - break; + if (!(pSprite->cstat & 0x101)) + { + nTarget = -1; + QueenList[nQueen].nTarget = -1; + QueenList[nQueen].nAction = 0; + } + } + } + switch (nAction) + { + case 0: + { + if (nTarget < 0) + { + nTarget = FindPlayer(nSprite, 60); } - case 0x20000: + if (nTarget >= 0) { - if (si < 3) { - Gravity(nSprite); - } + QueenList[nQueen].nAction = QueenList[nQueen].field_A + 1; + QueenList[nQueen].nFrame = 0; + QueenList[nQueen].nTarget = nTarget; + QueenList[nQueen].field_C = RandomSize(7); - short nSeq = SeqOffsets[kSeqQueen] + QueenSeq[nAction].a; + SetQueenSpeed(nSprite, si); + } + break; + } - pSprite->picnum = seq_GetSeqPicnum2(nSeq, QueenList[nQueen].nFrame); - - seq_MoveSequence(nSprite, nSeq, QueenList[nQueen].nFrame); - - QueenList[nQueen].nFrame++; - if (QueenList[nQueen].nFrame >= SeqSize[nSeq]) - { - QueenList[nQueen].nFrame = 0; - bVal = true; - } - - short nFlag = FrameFlag[SeqBase[nSeq] + QueenList[nQueen].nFrame]; - - if (nTarget > -1) - { - if (nAction < 7) - { - if (!(pSprite->cstat & 0x101)) - { - nTarget = -1; - QueenList[nQueen].nTarget = -1; - QueenList[nQueen].nAction = 0; - } - } - } - switch (nAction) - { - case 0: - { - if (nTarget < 0) - { - nTarget = FindPlayer(nSprite, 60); - } - - if (nTarget >= 0) - { - QueenList[nQueen].nAction = QueenList[nQueen].field_A + 1; - QueenList[nQueen].nFrame = 0; - QueenList[nQueen].nTarget = nTarget; - QueenList[nQueen].field_C = RandomSize(7); - - SetQueenSpeed(nSprite, si); - } - break; - } - - case 6: - { - if (bVal) - { - BuildQueenEgg(nQueen, 1); - QueenList[nQueen].nAction = 3; - QueenList[nQueen].field_C = RandomSize(6) + 60; - } - - break; - } - - case 1: - case 2: - case 3: - { - QueenList[nQueen].field_C--; - - if ((nQueen & 0x1F) == (totalmoves & 0x1F)) - { - if (si < 2) - { - if (QueenList[nQueen].field_C <= 0) - { - QueenList[nQueen].nFrame = 0; - pSprite->xvel = 0; - pSprite->yvel = 0; - QueenList[nQueen].nAction = si + 4; - QueenList[nQueen].field_C = RandomSize(6) + 30; - break; - } - else - { - if (QueenList[nQueen].field_10 < 5) - { - QueenList[nQueen].field_10++; - } - - // then to PLOTSPRITE - } - } - else - { - if (QueenList[nQueen].field_C <= 0) - { - if (WaspCount() < 100) - { - QueenList[nQueen].nAction = 6; - QueenList[nQueen].nFrame = 0; - break; - } - else - { - QueenList[nQueen].field_C = 30000; - // then to PLOTSPRITE - } - } - } - - // loc_35B4B - PlotCourseToSprite(nSprite, nTarget); - SetQueenSpeed(nSprite, si); - } - - int nMov = MoveCreatureWithCaution(nSprite); - - switch (nMov & 0xC000) - { - case 0xC000: - if ((si == 2) && ((nMov & 0x3FFF) == nTarget)) - { - runlist_DamageEnemy(nTarget, nSprite, 5); - break; - } - fallthrough__; - case 0x8000: - pSprite->ang += 256; - pSprite->ang &= kAngleMask; - - SetQueenSpeed(nSprite, si); - break; - } - - // loc_35BD2 - if (nAction && nTarget != -1) - { - if (!(sprite[nTarget].cstat & 0x101)) - { - QueenList[nQueen].nAction = 0; - QueenList[nQueen].nFrame = 0; - QueenList[nQueen].field_C = 100; - QueenList[nQueen].nTarget = -1; - - pSprite->xvel = 0; - pSprite->yvel = 0; - } - } - - break; - } - - case 4: - case 5: - { - if (bVal && QueenList[nQueen].field_10 <= 0) - { - QueenList[nQueen].nAction = 0; - QueenList[nQueen].field_C = 15; - } - else - { - if (nFlag & 0x80) - { - QueenList[nQueen].field_10--; - - PlotCourseToSprite(nSprite, nTarget); - - if (!si) - { - BuildBullet(nSprite, 12, 0, 0, -1, pSprite->ang, nTarget + 10000, 1); - } - else - { - BuildQueenEgg(nQueen, 0); - } - } - } - - break; - } - - case 7: - { - if (bVal) - { - QueenList[nQueen].nAction = 0; - QueenList[nQueen].nFrame = 0; - } - - break; - } - - case 8: - case 9: - { - if (bVal) - { - if (nAction == 9) - { - QueenList[nQueen].field_C--; - if (QueenList[nQueen].field_C <= 0) - { - pSprite->cstat = 0; - - for (int i = 0; i < 20; i++) - { - short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; - - sprite[nChunkSprite].picnum = kQueenChunk + (i % 3); - sprite[nChunkSprite].xrepeat = 100; - sprite[nChunkSprite].yrepeat = 100; - } - - short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)); - - sprite[nChunkSprite].picnum = kTile3126; - sprite[nChunkSprite].yrepeat = 100; - sprite[nChunkSprite].xrepeat = 100; - - PlayFXAtXYZ( - StaticSound[kSound40], - pSprite->x, - pSprite->y, - pSprite->z, - pSprite->sectnum); - - BuildQueenHead(nQueen); - - QueenList[nQueen].nAction++; - } - } - else - QueenList[nQueen].nAction++; - } - - break; - } - - case 10: - { - pSprite->cstat &= 0xFEFE; - break; - } - } - break; + case 6: + { + if (bVal) + { + BuildQueenEgg(nQueen, 1); + QueenList[nQueen].nAction = 3; + QueenList[nQueen].field_C = RandomSize(6) + 60; } - case 0xA0000: + break; + } + + case 1: + case 2: + case 3: + { + QueenList[nQueen].field_C--; + + if ((nQueen & 0x1F) == (totalmoves & 0x1F)) { - if (sprite[nRadialSpr].statnum != 121 && (pSprite->cstat & 0x101) != 0) + if (si < 2) { - nDamage = runlist_CheckRadialDamage(nSprite); - - if (!nDamage) { - break; - } - } - else - break; - - fallthrough__; - } // fall through to case 0x80000 - - case 0x80000: - { - if (QueenList[nQueen].nHealth > 0) - { - QueenList[nQueen].nHealth -= dmgAdjust(nDamage); - - if (QueenList[nQueen].nHealth <= 0) + if (QueenList[nQueen].field_C <= 0) { + QueenList[nQueen].nFrame = 0; pSprite->xvel = 0; pSprite->yvel = 0; - pSprite->zvel = 0; - - QueenList[nQueen].field_A++; - - switch (QueenList[nQueen].field_A) - { - case 1: - QueenList[nQueen].nHealth = 4000; - QueenList[nQueen].nAction = 7; - - BuildAnim(-1, 36, 0, pSprite->x, pSprite->y, pSprite->z - 7680, pSprite->sectnum, pSprite->xrepeat, 4); - break; - case 2: - QueenList[nQueen].nHealth = 4000; - QueenList[nQueen].nAction = 7; - - DestroyAllEggs(); - break; - case 3: - QueenList[nQueen].nAction = 8; - QueenList[nQueen].nHealth = 0; - QueenList[nQueen].field_C = 5; - - nCreaturesKilled++; - break; - } - - QueenList[nQueen].nFrame = 0; + QueenList[nQueen].nAction = si + 4; + QueenList[nQueen].field_C = RandomSize(6) + 30; + break; } else { - if (si > 0 && !RandomSize(4)) + if (QueenList[nQueen].field_10 < 5) { - QueenList[nQueen].nAction = 7; - QueenList[nQueen].nFrame = 0; + QueenList[nQueen].field_10++; + } + + // then to PLOTSPRITE + } + } + else + { + if (QueenList[nQueen].field_C <= 0) + { + if (WaspCount() < 100) + { + QueenList[nQueen].nAction = 6; + QueenList[nQueen].nFrame = 0; + break; + } + else + { + QueenList[nQueen].field_C = 30000; + // then to PLOTSPRITE } } } + + // loc_35B4B + PlotCourseToSprite(nSprite, nTarget); + SetQueenSpeed(nSprite, si); + } + + int nMov = MoveCreatureWithCaution(nSprite); + + switch (nMov & 0xC000) + { + case 0xC000: + if ((si == 2) && ((nMov & 0x3FFF) == nTarget)) + { + runlist_DamageEnemy(nTarget, nSprite, 5); + break; + } + fallthrough__; + case 0x8000: + pSprite->ang += 256; + pSprite->ang &= kAngleMask; + + SetQueenSpeed(nSprite, si); break; } - case 0x90000: + // loc_35BD2 + if (nAction && nTarget != -1) { - seq_PlotSequence(nObject, SeqOffsets[kSeqQueen] + QueenSeq[nAction].a, QueenList[nQueen].nFrame, QueenSeq[nAction].b); - break; + if (!(sprite[nTarget].cstat & 0x101)) + { + QueenList[nQueen].nAction = 0; + QueenList[nQueen].nFrame = 0; + QueenList[nQueen].field_C = 100; + QueenList[nQueen].nTarget = -1; + + pSprite->xvel = 0; + pSprite->yvel = 0; + } } + + break; + } + + case 4: + case 5: + { + if (bVal && QueenList[nQueen].field_10 <= 0) + { + QueenList[nQueen].nAction = 0; + QueenList[nQueen].field_C = 15; } + else + { + if (nFlag & 0x80) + { + QueenList[nQueen].field_10--; + + PlotCourseToSprite(nSprite, nTarget); + + if (!si) + { + BuildBullet(nSprite, 12, 0, 0, -1, pSprite->ang, nTarget + 10000, 1); + } + else + { + BuildQueenEgg(nQueen, 0); + } + } + } + + break; + } + + case 7: + { + if (bVal) + { + QueenList[nQueen].nAction = 0; + QueenList[nQueen].nFrame = 0; + } + + break; + } + + case 8: + case 9: + { + if (bVal) + { + if (nAction == 9) + { + QueenList[nQueen].field_C--; + if (QueenList[nQueen].field_C <= 0) + { + pSprite->cstat = 0; + + for (int i = 0; i < 20; i++) + { + short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; + + sprite[nChunkSprite].picnum = kQueenChunk + (i % 3); + sprite[nChunkSprite].xrepeat = 100; + sprite[nChunkSprite].yrepeat = 100; + } + + short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)); + + sprite[nChunkSprite].picnum = kTile3126; + sprite[nChunkSprite].yrepeat = 100; + sprite[nChunkSprite].xrepeat = 100; + + PlayFXAtXYZ( + StaticSound[kSound40], + pSprite->x, + pSprite->y, + pSprite->z, + pSprite->sectnum); + + BuildQueenHead(nQueen); + + QueenList[nQueen].nAction++; + } + } + else + QueenList[nQueen].nAction++; + } + + break; + } + + case 10: + { + pSprite->cstat &= 0xFEFE; + break; + } + } } + +void AIQueen::RadialDamage(RunListEvent* ev) +{ + short nQueen = RunData[ev->nRun].nVal; + assert(nQueen >= 0 && nQueen < kMaxQueens); + short nSprite = QueenList[nQueen].nSprite; + auto pSprite = &sprite[nSprite]; + + if (sprite[nRadialSpr].statnum != 121 && (pSprite->cstat & 0x101) != 0) + { + ev->nDamage = runlist_CheckRadialDamage(nSprite); + if (ev->nDamage) Damage(ev); + } +} + +void AIQueen::Damage(RunListEvent* ev) +{ + short nQueen = RunData[ev->nRun].nVal; + assert(nQueen >= 0 && nQueen < kMaxQueens); + + short nSprite = QueenList[nQueen].nSprite; + auto pSprite = &sprite[nSprite]; + short si = QueenList[nQueen].field_A; + + if (QueenList[nQueen].nHealth > 0) + { + QueenList[nQueen].nHealth -= dmgAdjust(ev->nDamage); + + if (QueenList[nQueen].nHealth <= 0) + { + pSprite->xvel = 0; + pSprite->yvel = 0; + pSprite->zvel = 0; + + QueenList[nQueen].field_A++; + + switch (QueenList[nQueen].field_A) + { + case 1: + QueenList[nQueen].nHealth = 4000; + QueenList[nQueen].nAction = 7; + + BuildAnim(-1, 36, 0, pSprite->x, pSprite->y, pSprite->z - 7680, pSprite->sectnum, pSprite->xrepeat, 4); + break; + case 2: + QueenList[nQueen].nHealth = 4000; + QueenList[nQueen].nAction = 7; + + DestroyAllEggs(); + break; + case 3: + QueenList[nQueen].nAction = 8; + QueenList[nQueen].nHealth = 0; + QueenList[nQueen].field_C = 5; + + nCreaturesKilled++; + break; + } + + QueenList[nQueen].nFrame = 0; + } + else + { + if (si > 0 && !RandomSize(4)) + { + QueenList[nQueen].nAction = 7; + QueenList[nQueen].nFrame = 0; + } + } + } +} + +void AIQueen::Draw(RunListEvent* ev) +{ + short nQueen = RunData[ev->nRun].nVal; + assert(nQueen >= 0 && nQueen < kMaxQueens); + short nAction = QueenList[nQueen].nAction; + seq_PlotSequence(ev->nIndex, SeqOffsets[kSeqQueen] + QueenSeq[nAction].a, QueenList[nQueen].nFrame, QueenSeq[nAction].b); +} + +void FuncQueen(int nObject, int nMessage, int nDamage, int nRun) +{ + AIQueen ai; + runlist_DispatchEvent(&ai, nObject, nMessage, nDamage, nRun); +} + END_PS_NS