//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 sirlemonhead, Nuke.YKT This file is part of PCExhumed. PCExhumed is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" #include "engine.h" #include "exhumed.h" #include "aistuff.h" #include "player.h" #include "view.h" #include "status.h" #include "sound.h" #include #include BEGIN_PS_NS short NearSector[kMaxSectors] = { 0 }; short nPushBlocks; // TODO - moveme? short overridesect; short NearCount = -1; short nBodySprite[50]; int hihit, sprceiling, sprfloor, lohit; #define kMaxPushBlocks 100 #define kMaxChunks 75 // think this belongs in init.c? BlockInfo sBlockInfo[kMaxPushBlocks]; short nChunkSprite[kMaxChunks]; static SavegameHelper sgh("move", SV(nPushBlocks), SV(overridesect), SV(NearCount), SV(hihit), SV(sprceiling), SV(sprfloor), SV(lohit), SA(nBodySprite), SA(NearSector), SA(sBlockInfo), SA(nChunkSprite), nullptr); signed int lsqrt(int a1) { int v1; int v2; signed int result; v1 = a1; v2 = a1 - 0x40000000; result = 0; if (v2 >= 0) { result = 32768; v1 = v2; } if (v1 - ((result << 15) + 0x10000000) >= 0) { v1 -= (result << 15) + 0x10000000; result += 16384; } if (v1 - ((result << 14) + 0x4000000) >= 0) { v1 -= (result << 14) + 0x4000000; result += 8192; } if (v1 - ((result << 13) + 0x1000000) >= 0) { v1 -= (result << 13) + 0x1000000; result += 4096; } if (v1 - ((result << 12) + 0x400000) >= 0) { v1 -= (result << 12) + 0x400000; result += 2048; } if (v1 - ((result << 11) + 0x100000) >= 0) { v1 -= (result << 11) + 0x100000; result += 1024; } if (v1 - ((result << 10) + 0x40000) >= 0) { v1 -= (result << 10) + 0x40000; result += 512; } if (v1 - ((result << 9) + 0x10000) >= 0) { v1 -= (result << 9) + 0x10000; result += 256; } if (v1 - ((result << 8) + 0x4000) >= 0) { v1 -= (result << 8) + 0x4000; result += 128; } if (v1 - ((result << 7) + 4096) >= 0) { v1 -= (result << 7) + 4096; result += 64; } if (v1 - ((result << 6) + 1024) >= 0) { v1 -= (result << 6) + 1024; result += 32; } if (v1 - (32 * result + 256) >= 0) { v1 -= 32 * result + 256; result += 16; } if (v1 - (16 * result + 64) >= 0) { v1 -= 16 * result + 64; result += 8; } if (v1 - (8 * result + 16) >= 0) { v1 -= 8 * result + 16; result += 4; } if (v1 - (4 * result + 4) >= 0) { v1 -= 4 * result + 4; result += 2; } if (v1 - (2 * result + 1) >= 0) result += 1; return result; } void MoveThings() { UndoFlashes(); DoLights(); if (nFreeze) { if (nFreeze == 1 || nFreeze == 2) { DoSpiritHead(); } } else { runlist_ExecObjects(); runlist_CleanRunRecs(); } MoveStatus(); DoBubbleMachines(); DoDrips(); DoMovingSects(); DoRegenerates(); if (levelnum == kMap20) { DoFinale(); if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart) { nDronePitch += 64; BendAmbientSound(); } } } void ResetMoveFifo() { localclock = (int)totalclock; movefifoend = 0; movefifopos = 0; } // not used void clipwall() { } void BuildNear(int x, int y, int walldist, int nSector) { NearSector[0] = nSector; NearCount = 1; int i = 0; while (i < NearCount) { short nSector = NearSector[i]; short nWall = sector[nSector].wallptr; short nWallCount = sector[nSector].wallnum; while (1) { nWallCount--; if (nWallCount < 0) { i++; break; } short nNextSector = wall[nWall].nextsector; if (nNextSector >= 0) { int j = 0; for (; j < NearCount; j++) { // loc_14F4D: if (nNextSector == NearSector[j]) break; } if (j >= NearCount) { vec2_t pos = { x, y }; if (clipinsidebox(&pos, nWall, walldist)) { NearSector[NearCount] = wall[nWall].nextsector; NearCount++; } } } nWall++; } } } int BelowNear(short nSprite) { short nSector = sprite[nSprite].sectnum; int z = sprite[nSprite].z; int var_24, z2; if ((lohit & 0xC000) == 0xC000) { var_24 = lohit & 0xC000; z2 = sprite[lohit & 0x3FFF].z; } else { var_24 = 0x20000; z2 = sector[nSector].floorz + SectDepth[nSector]; if (NearCount > 0) { short edx; for (int i = 0; i < NearCount; i++) { int nSect2 = NearSector[i]; while (nSect2 >= 0) { edx = nSect2; nSect2 = SectBelow[nSect2]; } int ecx = sector[edx].floorz + SectDepth[edx]; int eax = ecx - z; if (eax < 0 && eax >= -5120) { z2 = ecx; nSector = edx; } } } } if (z2 < sprite[nSprite].z) { sprite[nSprite].z = z2; overridesect = nSector; sprite[nSprite].zvel = 0; bTouchFloor = true; return var_24; } else { return 0; } } int movespritez(short nSprite, int z, int height, int UNUSED(flordist), int clipdist) { spritetype* pSprite = &sprite[nSprite]; short nSector = pSprite->sectnum; assert(nSector >= 0 && nSector < kMaxSectors); overridesect = nSector; short edi = nSector; // backup cstat uint16_t cstat = pSprite->cstat; pSprite->cstat &= ~CSTAT_SPRITE_BLOCK; int nRet = 0; short nSectFlags = SectFlag[nSector]; if (nSectFlags & kSectUnderwater) { z >>= 1; } int spriteZ = pSprite->z; int floorZ = sector[nSector].floorz; int ebp = spriteZ + z; int eax = sector[nSector].ceilingz + (height >> 1); if ((nSectFlags & kSectUnderwater) && ebp < eax) { ebp = eax; } // loc_151E7: while (ebp > sector[pSprite->sectnum].floorz && SectBelow[pSprite->sectnum] >= 0) { edi = SectBelow[pSprite->sectnum]; mychangespritesect(nSprite, edi); } if (edi != nSector) { pSprite->z = ebp; if (SectFlag[edi] & kSectUnderwater) { if (nSprite == PlayerList[nLocalPlayer].nSprite) { D3PlayFX(StaticSound[kSound2], nSprite); } if (pSprite->statnum <= 107) { pSprite->hitag = 0; } } } else { while ((ebp < sector[pSprite->sectnum].ceilingz) && (SectAbove[pSprite->sectnum] >= 0)) { edi = SectAbove[pSprite->sectnum]; mychangespritesect(nSprite, edi); } } // This function will keep the player from falling off cliffs when you're too close to the edge. // This function finds the highest and lowest z coordinates that your clipping BOX can get to. getzrange_old(pSprite->x, pSprite->y, pSprite->z - 256, pSprite->sectnum, &sprceiling, &hihit, &sprfloor, &lohit, 128, CLIPMASK0); int mySprfloor = sprfloor; if ((lohit & 0xC000) != 0xC000) { mySprfloor += SectDepth[pSprite->sectnum]; } if (ebp > mySprfloor) { if (z > 0) { bTouchFloor = true; if ((lohit & 0xC000) == 0xC000) { // Path A short nFloorSprite = lohit & 0x3FFF; if (pSprite->statnum == 100 && sprite[nFloorSprite].statnum != 0 && sprite[nFloorSprite].statnum < 100) { short nDamage = (z >> 9); if (nDamage) { runlist_DamageEnemy(nFloorSprite, nSprite, nDamage << 1); } pSprite->zvel = -z; } else { if (sprite[nFloorSprite].statnum == 0 || sprite[nFloorSprite].statnum > 199) { nRet |= 0x20000; } else { nRet |= lohit; } pSprite->zvel = 0; } } else { // Path B if (SectBelow[pSprite->sectnum] == -1) { nRet |= 0x20000; short nSectDamage = SectDamage[pSprite->sectnum]; if (nSectDamage != 0) { if (pSprite->hitag < 15) { IgniteSprite(nSprite); pSprite->hitag = 20; } nSectDamage >>= 2; nSectDamage = nSectDamage - (nSectDamage>>2); if (nSectDamage) { runlist_DamageEnemy(nSprite, -1, nSectDamage); } } pSprite->zvel = 0; } } } // loc_1543B: ebp = mySprfloor; pSprite->z = mySprfloor; } else { if ((ebp - height) < sprceiling && ((hihit & 0xC000) == 0xC000 || SectAbove[pSprite->sectnum] == -1)) { ebp = sprceiling + height; nRet |= 0x10000; } } if (spriteZ <= floorZ && ebp > floorZ) { if ((SectDepth[nSector] != 0) || (edi != nSector && (SectFlag[edi] & kSectUnderwater))) { assert(nSector >= 0 && nSector < kMaxSectors); BuildSplash(nSprite, nSector); } } pSprite->cstat = cstat; // restore cstat pSprite->z = ebp; if (pSprite->statnum == 100) { BuildNear(pSprite->x, pSprite->y, clipdist + (clipdist / 2), pSprite->sectnum); nRet |= BelowNear(nSprite); } return nRet; } int GetSpriteHeight(int nSprite) { return tilesiz[sprite[nSprite].picnum].y * sprite[nSprite].yrepeat * 4; } int movesprite(short nSprite, int dx, int dy, int dz, int UNUSED(ceildist), int flordist, unsigned int clipmask) { spritetype *pSprite = &sprite[nSprite]; bTouchFloor = false; int x = pSprite->x; int y = pSprite->y; int z = pSprite->z; int nSpriteHeight = GetSpriteHeight(nSprite); int nClipDist = (int8_t)pSprite->clipdist << 2; short nSector = pSprite->sectnum; assert(nSector >= 0 && nSector < kMaxSectors); int floorZ = sector[nSector].floorz; int nRet = 0; if ((SectFlag[nSector] & kSectUnderwater) || (floorZ < z)) { dx >>= 1; dy >>= 1; } nRet |= movespritez(nSprite, dz, nSpriteHeight, flordist, nClipDist); nSector = pSprite->sectnum; // modified in movespritez so re-grab this variable if (pSprite->statnum == 100) { short nPlayer = GetPlayerFromSprite(nSprite); int varA = 0; int varB = 0; CheckSectorFloor(overridesect, pSprite->z, &varB, &varA); if (varB || varA) { nXDamage[nPlayer] = varB; nYDamage[nPlayer] = varA; } dx += nXDamage[nPlayer]; dy += nYDamage[nPlayer]; } else { CheckSectorFloor(overridesect, pSprite->z, &dx, &dy); } nRet |= (uint16_t)clipmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, dx, dy, nClipDist, nSpriteHeight, flordist, clipmask); if ((nSector != pSprite->sectnum) && nSector >= 0) { if (nRet & 0x20000) { dz = 0; } if ((sector[nSector].floorz - z) < (dz + flordist)) { pSprite->x = x; pSprite->y = y; } else { mychangespritesect(nSprite, nSector); if (pSprite->pal < 5 && !pSprite->hitag) { pSprite->pal = sector[pSprite->sectnum].ceilingpal; } } } return nRet; } void Gravity(short nSprite) { short nSector = sprite[nSprite].sectnum; if (SectFlag[nSector] & kSectUnderwater) { if (sprite[nSprite].statnum != 100) { if (sprite[nSprite].zvel <= 1024) { if (sprite[nSprite].zvel < 2048) { sprite[nSprite].zvel += 512; } } else { sprite[nSprite].zvel -= 64; } } else { if (sprite[nSprite].zvel > 0) { sprite[nSprite].zvel -= 64; if (sprite[nSprite].zvel < 0) { sprite[nSprite].zvel = 0; } } else if (sprite[nSprite].zvel < 0) { sprite[nSprite].zvel += 64; if (sprite[nSprite].zvel > 0) { sprite[nSprite].zvel = 0; } } } } else { sprite[nSprite].zvel += 512; if (sprite[nSprite].zvel > 16384) { sprite[nSprite].zvel = 16384; } } } int MoveCreature(short nSprite) { return movesprite(nSprite, sprite[nSprite].xvel << 8, sprite[nSprite].yvel << 8, sprite[nSprite].zvel, 15360, -5120, CLIPMASK0); } int MoveCreatureWithCaution(int nSprite) { int x = sprite[nSprite].x; int y = sprite[nSprite].y; int z = sprite[nSprite].z; short nSectorPre = sprite[nSprite].sectnum; int ecx = MoveCreature(nSprite); short nSector = sprite[nSprite].sectnum; if (nSector != nSectorPre) { int zDiff = sector[nSectorPre].floorz - sector[nSector].floorz; if (zDiff < 0) { zDiff = -zDiff; } if (zDiff > 15360 || (SectFlag[nSector] & kSectUnderwater) || (SectBelow[nSector] > -1 && SectFlag[SectBelow[nSector]]) || SectDamage[nSector]) { sprite[nSprite].x = x; sprite[nSprite].y = y; sprite[nSprite].z = z; mychangespritesect(nSprite, nSectorPre); sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 2; sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; return 0; } } return ecx; } int GetAngleToSprite(int nSprite1, int nSprite2) { if (nSprite1 < 0 || nSprite2 < 0) return -1; return GetMyAngle(sprite[nSprite2].x - sprite[nSprite1].x, sprite[nSprite2].y - sprite[nSprite1].y); } int PlotCourseToSprite(int nSprite1, int nSprite2) { if (nSprite1 < 0 || nSprite2 < 0) return -1; int x = sprite[nSprite2].x - sprite[nSprite1].x; int y = sprite[nSprite2].y - sprite[nSprite1].y; sprite[nSprite1].ang = GetMyAngle(x, y); return ksqrt(y * y + x * x); } int FindPlayer(int nSprite, int nDistance) { int var_18 = 0; if (nSprite >= 0) var_18 = 1; if (nSprite < 0) nSprite = -nSprite; if (nDistance < 0) nDistance = 100; int x = sprite[nSprite].x; int y = sprite[nSprite].y; short nSector = sprite[nSprite].sectnum; int z = sprite[nSprite].z - GetSpriteHeight(nSprite); nDistance <<= 8; short nPlayerSprite; int i = 0; while (1) { if (i >= nTotalPlayers) return -1; nPlayerSprite = PlayerList[i].nSprite; if ((sprite[nPlayerSprite].cstat & 0x101) && (!(sprite[nPlayerSprite].cstat & 0x8000))) { int v9 = klabs(sprite[nPlayerSprite].x - x); if (v9 < nDistance) { int v10 = klabs(sprite[nPlayerSprite].y - y); if (v10 < nDistance && cansee(sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z - 7680, sprite[nPlayerSprite].sectnum, x, y, z, nSector)) { break; } } } i++; } if (var_18) { PlotCourseToSprite(nSprite, nPlayerSprite); } return nPlayerSprite; } void CheckSectorFloor(short nSector, int z, int *x, int *y) { short nSpeed = SectSpeed[nSector]; if (!nSpeed) { return; } short nFlag = SectFlag[nSector]; short nAng = nFlag & kAngleMask; if (z >= sector[nSector].floorz) { *x += (Cos(nAng) << 3) * nSpeed; *y += (sintable[nAng] << 3) * nSpeed; // no anglemask in original code } else if (nFlag & 0x800) { *x += (Cos(nAng) << 4) * nSpeed; *y += (sintable[nAng] << 4) * nSpeed; // no anglemask in original code } } int GetUpAngle(short nSprite1, int nVal, short nSprite2, int ecx) { int x = sprite[nSprite2].x - sprite[nSprite1].x; int y = sprite[nSprite2].y - sprite[nSprite1].y; int ebx = (sprite[nSprite2].z + ecx) - (sprite[nSprite1].z + nVal); int edx = (sprite[nSprite2].z + ecx) - (sprite[nSprite1].z + nVal); ebx >>= 4; edx >>= 8; ebx = -ebx; ebx -= edx; int nSqrt = lsqrt(x * x + y * y); return GetMyAngle(nSqrt, ebx); } void InitPushBlocks() { nPushBlocks = 0; } int GrabPushBlock() { if (nPushBlocks >= kMaxPushBlocks) { return -1; } return nPushBlocks++; } void CreatePushBlock(int nSector) { int nBlock = GrabPushBlock(); int i; int startwall = sector[nSector].wallptr; int nWalls = sector[nSector].wallnum; int xSum = 0; int ySum = 0; for (i = 0; i < nWalls; i++) { xSum += wall[startwall + i].x; ySum += wall[startwall + i].y; } int xAvg = xSum / nWalls; int yAvg = ySum / nWalls; sBlockInfo[nBlock].x = xAvg; sBlockInfo[nBlock].y = yAvg; int nSprite = insertsprite(nSector, 0); sBlockInfo[nBlock].nSprite = nSprite; sprite[nSprite].x = xAvg; sprite[nSprite].y = yAvg; sprite[nSprite].z = sector[nSector].floorz - 256; sprite[nSprite].cstat = 0x8000; int var_28 = 0; for (i = 0; i < nWalls; i++) { int x = xAvg - wall[startwall + i].x; int y = yAvg - wall[startwall + i].y; int nSqrt = ksqrt(x * x + y * y); if (nSqrt > var_28) { var_28 = nSqrt; } } sBlockInfo[nBlock].field_8 = var_28; sprite[nSprite].clipdist = (var_28 & 0xFF) << 2; sector[nSector].extra = nBlock; } void MoveSector(short nSector, int nAngle, int *nXVel, int *nYVel) { int i; if (nSector == -1) { return; } int nXVect, nYVect; if (nAngle < 0) { nXVect = *nXVel; nYVect = *nYVel; nAngle = GetMyAngle(nXVect, nYVect); } else { nXVect = Sin(nAngle + 512) << 6; nYVect = Sin(nAngle) << 6; } short nBlock = sector[nSector].extra; short nSectFlag = SectFlag[nSector]; sectortype *pSector = §or[nSector]; int nFloorZ = sector[nSector].floorz; int startwall = sector[nSector].wallptr; int nWalls = sector[nSector].wallnum; walltype *pStartWall = &wall[startwall]; short nNextSector = wall[startwall].nextsector; BlockInfo *pBlockInfo = &sBlockInfo[nBlock]; int x = sBlockInfo[nBlock].x; int x_b = sBlockInfo[nBlock].x; int y = sBlockInfo[nBlock].y; int y_b = sBlockInfo[nBlock].y; short nSectorB = nSector; int nZVal; int z; int bUnderwater = nSectFlag & kSectUnderwater; if (nSectFlag & kSectUnderwater) { nZVal = sector[nSector].ceilingz; z = sector[nNextSector].ceilingz + 256; sector[nSector].ceilingz = sector[nNextSector].ceilingz; } else { nZVal = sector[nSector].floorz; z = sector[nNextSector].floorz - 256; sector[nSector].floorz = sector[nNextSector].floorz; } clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, nXVect, nYVect, pBlockInfo->field_8, 0, 0, CLIPMASK1); int yvect = y - y_b; int xvect = x - x_b; if (nSectorB != nNextSector && nSectorB != nSector) { yvect = 0; xvect = 0; } else { if (!bUnderwater) { z = nZVal; x = x_b; y = y_b; clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, nXVect, nYVect, pBlockInfo->field_8, 0, 0, CLIPMASK1); int ebx = x; int ecx = x_b; int edx = y; int eax = xvect; int esi = y_b; if (eax < 0) { eax = -eax; } ebx -= ecx; ecx = eax; eax = ebx; edx -= esi; if (eax < 0) { eax = -eax; } if (ecx > eax) { xvect = ebx; } eax = yvect; if (eax < 0) { eax = -eax; } ebx = eax; eax = edx; if (eax < 0) { eax = -eax; } if (ebx > eax) { yvect = edx; } } } // GREEN if (yvect || xvect) { for (i = headspritesect[nSector]; i != -1; i = nextspritesect[i]) { if (sprite[i].statnum < 99) { sprite[i].x += xvect; sprite[i].y += yvect; } else { z = sprite[i].z; if ((nSectFlag & kSectUnderwater) || z != nZVal || sprite[i].cstat & 0x8000) { x = sprite[i].x; y = sprite[i].y; nSectorB = nSector; clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, -xvect, -yvect, 4 * sprite[i].clipdist, 0, 0, CLIPMASK0); if (nSectorB >= 0 && nSectorB < kMaxSectors && nSectorB != nSector) { mychangespritesect(i, nSectorB); } } } } for (i = headspritesect[nNextSector]; i != -1; i = nextspritesect[i]) { if (sprite[i].statnum >= 99) { x = sprite[i].x; y = sprite[i].y; z = sprite[i].z; nSectorB = nNextSector; clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, -xvect - (Sin(nAngle + 512) * (4 * sprite[i].clipdist)), -yvect - (Sin(nAngle) * (4 * sprite[i].clipdist)), 4 * sprite[i].clipdist, 0, 0, CLIPMASK0); if (nSectorB != nNextSector && (nSectorB == nSector || nNextSector == nSector)) { if (nSectorB != nSector || nFloorZ >= sprite[i].z) { if (nSectorB >= 0 && nSectorB < kMaxSectors) { mychangespritesect(i, nSectorB); } } else { movesprite(i, (xvect << 14) + Sin(nAngle + 512) * sprite[i].clipdist, (yvect << 14) + Sin(nAngle) * sprite[i].clipdist, 0, 0, 0, CLIPMASK0); } } } } for (int i = 0; i < nWalls; i++) { dragpoint(startwall, xvect + pStartWall->x, yvect + pStartWall->y, 0); pStartWall++; startwall++; } pBlockInfo->x += xvect; pBlockInfo->y += yvect; } // loc_163DD xvect <<= 14; yvect <<= 14; if (!(nSectFlag & kSectUnderwater)) { for (i = headspritesect[nSector]; i != -1; i = nextspritesect[i]) { if (sprite[i].statnum >= 99 && nZVal == sprite[i].z && !(sprite[i].cstat & 0x8000)) { nSectorB = nSector; clipmove_old(&sprite[i].x, &sprite[i].y, &sprite[i].z, &nSectorB, xvect, yvect, 4 * sprite[i].clipdist, 5120, -5120, CLIPMASK0); } } } if (nSectFlag & kSectUnderwater) { pSector->ceilingz = nZVal; } else { pSector->floorz = nZVal; } *nXVel = xvect; *nYVel = yvect; /* Update player position variables, in case the player sprite was moved by a sector, Otherwise these can be out of sync when used in sound code (before being updated in PlayerFunc()). Can cause local player sounds to play off-centre. TODO: Might need to be done elsewhere too? */ int nPlayerSprite = PlayerList[nLocalPlayer].nSprite; initx = sprite[nPlayerSprite].x; inity = sprite[nPlayerSprite].y; initz = sprite[nPlayerSprite].z; inita = sprite[nPlayerSprite].ang; initsect = sprite[nPlayerSprite].sectnum; } void SetQuake(short nSprite, int nVal) { int x = sprite[nSprite].x; int y = sprite[nSprite].y; nVal *= 256; for (int i = 0; i < nTotalPlayers; i++) { int nPlayerSprite = PlayerList[i].nSprite; int xDiff = sprite[nPlayerSprite].x - x; int yDiff = sprite[nPlayerSprite].y - y; int nSqrt = ksqrt((xDiff >> 8)* (xDiff >> 8) + (yDiff >> 8)* (yDiff >> 8)); int eax = nVal; if (nSqrt) { eax = eax / nSqrt; if (eax >= 256) { if (eax > 3840) { eax = 3840; } } else { eax = 0; } } if (eax > nQuake[i]) { nQuake[i] = eax; } } } int AngleChase(int nSprite, int nSprite2, int ebx, int ecx, int push1) { int nClipType = sprite[nSprite].statnum != 107; /* bjd - need to handle cliptype to clipmask change that occured in later build engine version */ if (nClipType == 1) { nClipType = CLIPMASK1; } else { nClipType = CLIPMASK0; } short nAngle; if (nSprite2 < 0) { sprite[nSprite].zvel = 0; nAngle = sprite[nSprite].ang; } else { int nHeight = tilesiz[sprite[nSprite2].picnum].y * sprite[nSprite2].yrepeat * 2; int xDiff = sprite[nSprite2].x - sprite[nSprite].x; int yDiff = sprite[nSprite2].y - sprite[nSprite].y; int nMyAngle = GetMyAngle(xDiff, yDiff); int nSqrt = ksqrt(xDiff * xDiff + yDiff * yDiff); int var_18 = GetMyAngle(nSqrt, ((sprite[nSprite2].z - nHeight) - sprite[nSprite].z) >> 8); int nAngDelta = AngleDelta(sprite[nSprite].ang, nMyAngle, 1024); int nAngDelta2 = klabs(nAngDelta); if (nAngDelta2 > 63) { nAngDelta2 = klabs(nAngDelta >> 6); ebx /= nAngDelta2; if (ebx < 5) { ebx = 5; } } int nAngDeltaC = klabs(nAngDelta); if (nAngDeltaC > push1) { if (nAngDelta >= 0) nAngDelta = push1; else nAngDelta = -push1; } nAngle = (nAngDelta + sprite[nSprite].ang) & kAngleMask; int nAngDeltaD = AngleDelta(sprite[nSprite].zvel, var_18, 24); sprite[nSprite].zvel = (sprite[nSprite].zvel + nAngDeltaD) & kAngleMask; } sprite[nSprite].ang = nAngle; int eax = klabs(Cos(sprite[nSprite].zvel)); int x = ((Cos(nAngle) * ebx) >> 14) * eax; int y = ((Sin(nAngle) * ebx) >> 14) * eax; int xshift = x >> 8; int yshift = y >> 8; int z = Sin(sprite[nSprite].zvel) * ksqrt((xshift * xshift) + (yshift * yshift)); return movesprite(nSprite, x >> 2, y >> 2, (z >> 13) + (Sin(ecx) >> 5), 0, 0, nClipType); } int GetWallNormal(short nWall) { nWall &= kMaxWalls-1; int nWall2 = wall[nWall].point2; int nAngle = GetMyAngle(wall[nWall2].x - wall[nWall].x, wall[nWall2].y - wall[nWall].y); return (nAngle + 512) & kAngleMask; } void WheresMyMouth(int nPlayer, int *x, int *y, int *z, short *sectnum) { int nSprite = PlayerList[nPlayer].nSprite; *x = sprite[nSprite].x; *y = sprite[nSprite].y; int height = GetSpriteHeight(nSprite) / 2; *z = sprite[nSprite].z - height; *sectnum = sprite[nSprite].sectnum; clipmove_old((int32_t*)x, (int32_t*)y, (int32_t*)z, sectnum, Cos(sprite[nSprite].ang) << 7, Sin(sprite[nSprite].ang) << 7, 5120, 1280, 1280, CLIPMASK1); } void InitChunks() { nCurChunkNum = 0; memset(nChunkSprite, -1, sizeof(nChunkSprite)); memset(nBodyGunSprite, -1, sizeof(nBodyGunSprite)); memset(nBodySprite, -1, sizeof(nBodySprite)); nCurBodyNum = 0; nCurBodyGunNum = 0; nBodyTotal = 0; nChunkTotal = 0; } int GrabBodyGunSprite() { int nSprite = nBodyGunSprite[nCurBodyGunNum]; if (nSprite == -1) { nSprite = insertsprite(0, 899); nBodyGunSprite[nCurBodyGunNum] = nSprite; sprite[nSprite].lotag = -1; sprite[nSprite].owner = -1; } else { int nAnim = sprite[nSprite].owner; if (nAnim != -1) { DestroyAnim(nAnim); } sprite[nSprite].lotag = -1; sprite[nSprite].owner = -1; } nCurBodyGunNum++; if (nCurBodyGunNum >= 50) { // TODO - enum/define nCurBodyGunNum = 0; } sprite[nSprite].cstat = 0; return nSprite; } int GrabBody() { int nSprite; do { nSprite = nBodySprite[nCurBodyNum]; if (nSprite == -1) { nSprite = insertsprite(0, 899); nBodySprite[nCurBodyNum] = nSprite; sprite[nSprite].cstat = 0x8000; } nCurBodyNum++; if (nCurBodyNum >= 50) { nCurBodyNum = 0; } } while (sprite[nSprite].cstat & 0x101); if (nBodyTotal < 50) { nBodyTotal++; } sprite[nSprite].cstat = 0; return nSprite; } int GrabChunkSprite() { int nSprite = nChunkSprite[nCurChunkNum]; if (nSprite == -1) { nSprite = insertsprite(0, 899); nChunkSprite[nCurChunkNum] = nSprite; } else if (sprite[nSprite].statnum) { // TODO MonoOut("too many chunks being used at once!\n"); return -1; } changespritestat(nSprite, 899); nCurChunkNum++; if (nCurChunkNum >= kMaxChunks) nCurChunkNum = 0; if (nChunkTotal < kMaxChunks) nChunkTotal++; sprite[nSprite].cstat = 0x80; return nSprite; } int BuildCreatureChunk(int nVal, int nPic) { int var_14; int nSprite = GrabChunkSprite(); if (nSprite == -1) { return -1; } if (nVal & 0x4000) { nVal &= 0x3FFF; var_14 = 1; } else { var_14 = 0; } nVal &= 0xFFFF; sprite[nSprite].x = sprite[nVal].x; sprite[nSprite].y = sprite[nVal].y; sprite[nSprite].z = sprite[nVal].z; mychangespritesect(nSprite, sprite[nVal].sectnum); sprite[nSprite].cstat = 0x80; sprite[nSprite].shade = -12; sprite[nSprite].pal = 0; sprite[nSprite].xvel = (RandomSize(5) - 16) << 7; sprite[nSprite].yvel = (RandomSize(5) - 16) << 7; sprite[nSprite].zvel = (-(RandomSize(8) + 512)) << 3; if (var_14) { sprite[nSprite].xvel *= 4; sprite[nSprite].yvel *= 4; sprite[nSprite].zvel *= 2; } sprite[nSprite].xrepeat = 64; sprite[nSprite].yrepeat = 64; sprite[nSprite].xoffset = 0; sprite[nSprite].yoffset = 0; sprite[nSprite].picnum = nPic; sprite[nSprite].lotag = runlist_HeadRun() + 1; sprite[nSprite].clipdist = 40; // GrabTimeSlot(3); sprite[nSprite].extra = -1; sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nSprite | 0xD0000); sprite[nSprite].hitag = runlist_AddRunRec(NewRun, nSprite | 0xD0000); return nSprite | 0xD0000; } void FuncCreatureChunk(int a, int, int nRun) { int nSprite = RunData[nRun].nVal; assert(nSprite >= 0 && nSprite < kMaxSprites); int nMessage = a & 0x7F0000; if (nMessage != 0x20000) return; Gravity(nSprite); int nSector = sprite[nSprite].sectnum; sprite[nSprite].pal = sector[nSector].ceilingpal; int nVal = movesprite(nSprite, sprite[nSprite].xvel << 10, sprite[nSprite].yvel << 10, sprite[nSprite].zvel, 2560, -2560, CLIPMASK1); if (sprite[nSprite].z >= sector[nSector].floorz) { // re-grab this variable as it may have changed in movesprite(). Note the check above is against the value *before* movesprite so don't change it. nSector = sprite[nSprite].sectnum; sprite[nSprite].xvel = 0; sprite[nSprite].yvel = 0; sprite[nSprite].zvel = 0; sprite[nSprite].z = sector[nSector].floorz; } else { if (!nVal) return; short nAngle; if (nVal & 0x20000) { sprite[nSprite].cstat = 0x8000; } else { if ((nVal & 0x3C000) == 0x10000) { sprite[nSprite].xvel >>= 1; sprite[nSprite].yvel >>= 1; sprite[nSprite].zvel = -sprite[nSprite].zvel; return; } else if ((nVal & 0x3C000) == 0xC000) { nAngle = sprite[nVal & 0x3FFF].ang; } else if ((nVal & 0x3C000) == 0x8000) { nAngle = GetWallNormal(nVal & 0x3FFF); } else { return; } // loc_16E0C int nSqrt = lsqrt(((sprite[nSprite].yvel >> 10) * (sprite[nSprite].yvel >> 10) + (sprite[nSprite].xvel >> 10) * (sprite[nSprite].xvel >> 10)) >> 8); sprite[nSprite].xvel = Cos(nAngle) * (nSqrt >> 1); sprite[nSprite].yvel = Sin(nAngle) * (nSqrt >> 1); return; } } runlist_DoSubRunRec(sprite[nSprite].owner); runlist_FreeRun(sprite[nSprite].lotag - 1); runlist_SubRunRec(sprite[nSprite].hitag); changespritestat(nSprite, 0); sprite[nSprite].hitag = 0; sprite[nSprite].lotag = 0; } short UpdateEnemy(short *nEnemy) { if (*nEnemy >= 0) { if (!(sprite[*nEnemy].cstat & 0x101)) { *nEnemy = -1; } } return *nEnemy; } END_PS_NS