raze/source/games/blood/src/gameutil.cpp

746 lines
24 KiB
C++
Raw Normal View History

2019-09-19 22:42:45 +00:00
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood 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" // Must come before everything else!
2019-09-19 22:42:45 +00:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2019-09-19 22:42:45 +00:00
#include "build.h"
#include "blood.h"
2019-09-19 22:42:45 +00:00
BEGIN_BLD_NS
HitInfo gHitInfo;
2019-09-19 22:42:45 +00:00
2021-11-24 00:30:05 +00:00
bool FindSector(int nX, int nY, int nZ, sectortype** pSector)
2019-09-19 22:42:45 +00:00
{
int32_t nZFloor, nZCeil;
2021-11-24 00:30:05 +00:00
assert(pSector);
if (inside(nX, nY, *pSector))
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
getzsofslopeptr(*pSector, nX, nY, &nZCeil, &nZFloor);
2019-09-19 22:42:45 +00:00
if (nZ >= nZCeil && nZ <= nZFloor)
{
return 1;
}
}
2021-11-24 00:30:05 +00:00
for (auto& wal : wallsofsector(*pSector))
2019-09-19 22:42:45 +00:00
{
auto pOSector = wal.nextSector();
if (pOSector != nullptr && inside(nX, nY, pOSector))
2019-09-19 22:42:45 +00:00
{
getzsofslopeptr(pOSector, nX, nY, &nZCeil, &nZFloor);
2019-09-19 22:42:45 +00:00
if (nZ >= nZCeil && nZ <= nZFloor)
{
2021-11-24 00:30:05 +00:00
*pSector = pOSector;
2019-09-19 22:42:45 +00:00
return 1;
}
}
}
for(auto& sec: sector)
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
if (inside(nX, nY, &sec))
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
getzsofslopeptr(&sec, nX, nY, &nZCeil, &nZFloor);
2019-09-19 22:42:45 +00:00
if (nZ >= nZCeil && nZ <= nZFloor)
{
2021-11-24 00:30:05 +00:00
*pSector = &sec;
2019-09-19 22:42:45 +00:00
return 1;
}
}
}
return 0;
}
2021-11-24 00:30:05 +00:00
bool FindSector(int nX, int nY, sectortype** pSector)
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
assert(*pSector);
if (inside(nX, nY, *pSector))
2019-09-19 22:42:45 +00:00
{
return 1;
}
2021-11-24 00:30:05 +00:00
for (auto& wal : wallsofsector(*pSector))
2019-09-19 22:42:45 +00:00
{
auto pOSector = wal.nextSector();
if (pOSector != nullptr && inside(nX, nY, pOSector))
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
*pSector = pOSector;
2019-09-19 22:42:45 +00:00
return 1;
}
}
for (auto& sec: sector)
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
if (inside(nX, nY, &sec))
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:30:05 +00:00
*pSector = &sec;
2019-09-19 22:42:45 +00:00
return 1;
}
}
return 0;
}
2021-11-23 23:52:54 +00:00
bool CheckProximity(DBloodActor *actor, int nX, int nY, int nZ, sectortype* pSector, int nDist)
2019-09-19 22:42:45 +00:00
{
assert(actor != NULL);
auto pSprite = &actor->s();
int oX = abs(nX-pSprite->x)>>4;
2019-09-19 22:42:45 +00:00
if (oX >= nDist) return 0;
int oY = abs(nY-pSprite->y)>>4;
2019-09-19 22:42:45 +00:00
if (oY >= nDist) return 0;
int oZ = abs(nZ-pSprite->z)>>8;
2019-09-19 22:42:45 +00:00
if (oZ >= nDist) return 0;
if (approxDist(oX, oY) >= nDist) return 0;
int bottom, top;
GetActorExtents(actor, &top, &bottom);
2021-11-23 23:52:54 +00:00
if (cansee(pSprite->x, pSprite->y, pSprite->z, pSprite->sector(), nX, nY, nZ, pSector))
2019-09-19 22:42:45 +00:00
return 1;
2021-11-23 23:52:54 +00:00
if (cansee(pSprite->x, pSprite->y, bottom, pSprite->sector(), nX, nY, nZ, pSector))
2019-09-19 22:42:45 +00:00
return 1;
2021-11-23 23:52:54 +00:00
if (cansee(pSprite->x, pSprite->y, top, pSprite->sector(), nX, nY, nZ, pSector))
2019-09-19 22:42:45 +00:00
return 1;
return 0;
}
bool CheckProximityPoint(int nX1, int nY1, int nZ1, int nX2, int nY2, int nZ2, int nDist)
{
int oX = abs(nX2-nX1)>>4;
2019-09-19 22:42:45 +00:00
if (oX >= nDist)
return 0;
int oY = abs(nY2-nY1)>>4;
2019-09-19 22:42:45 +00:00
if (oY >= nDist)
return 0;
if (nZ2 != nZ1)
{
int oZ = abs(nZ2-nZ1)>>8;
if (oZ >= nDist)
return 0;
}
2019-09-19 22:42:45 +00:00
if (approxDist(oX, oY) >= nDist) return 0;
return 1;
}
2021-11-21 16:10:38 +00:00
bool CheckProximityWall(walltype* pWall, int x, int y, int nDist)
2019-09-19 22:42:45 +00:00
{
2021-11-21 16:10:38 +00:00
int x1 = pWall->x;
int y1 = pWall->y;
int x2 = pWall->point2Wall()->x;
int y2 = pWall->point2Wall()->y;
2019-09-19 22:42:45 +00:00
nDist <<= 4;
if (x1 < x2)
{
if (x <= x1 - nDist || x >= x2 + nDist)
{
return 0;
}
}
else
{
if (x <= x2 - nDist || x >= x1 + nDist)
{
return 0;
}
if (x1 == x2)
{
int px1 = x - x1;
int py1 = y - y1;
int px2 = x - x2;
int py2 = y - y2;
int dist1 = px1 * px1 + py1 * py1;
int dist2 = px2 * px2 + py2 * py2;
if (y1 < y2)
{
if (y <= y1 - nDist || y >= y2 + nDist)
{
return 0;
}
if (y < y1)
{
return dist1 < nDist * nDist;
}
if (y > y2)
{
return dist2 < nDist * nDist;
}
}
else
{
if (y <= y2 - nDist || y >= y1 + nDist)
{
return 0;
}
if (y < y2)
{
return dist2 < nDist * nDist;
}
if (y > y1)
{
return dist1 < nDist * nDist;
}
}
return 1;
}
}
if (y1 < y2)
{
if (y <= y1 - nDist || y >= y2 + nDist)
{
return 0;
}
}
else
{
if (y <= y2 - nDist || y >= y1 + nDist)
{
return 0;
}
if (y1 == y2)
{
int px1 = x - x1;
int py1 = y - y1;
int px2 = x - x2;
int py2 = y - y2;
int check1 = px1 * px1 + py1 * py1;
int check2 = px2 * px2 + py2 * py2;
if (x1 < x2)
{
if (x <= x1 - nDist || x >= x2 + nDist)
{
return 0;
}
if (x < x1)
{
return check1 < nDist * nDist;
}
if (x > x2)
{
return check2 < nDist * nDist;
}
}
else
{
if (x <= x2 - nDist || x >= x1 + nDist)
{
return 0;
}
if (x < x2)
{
return check2 < nDist * nDist;
}
if (x > x1)
{
return check1 < nDist * nDist;
}
}
}
}
int dx = x2 - x1;
int dy = y2 - y1;
int px = x - x2;
int py = y - y2;
int side = px * dx + dy * py;
if (side >= 0)
{
return px * px + py * py < nDist * nDist;
}
px = x - x1;
py = y - y1;
side = px * dx + dy * py;
if (side <= 0)
{
return px * px + py * py < nDist * nDist;
}
int check1 = px * dy - dx * py;
int check2 = dy * dy + dx * dx;
return check1 * check1 < check2 * nDist * nDist;
}
2020-12-03 17:00:07 +00:00
int GetWallAngle(walltype* pWall)
{
2021-11-21 08:45:50 +00:00
auto delta = pWall->delta();
2021-12-22 09:26:51 +00:00
return getangle(delta.X, delta.y);
2020-12-03 17:00:07 +00:00
}
void GetWallNormal(walltype* pWall, int *pX, int *pY)
2019-09-19 22:42:45 +00:00
{
auto delta = pWall->delta();
int dX = -delta.y >> 4;
2021-12-22 09:26:51 +00:00
int dY = delta.X >> 4;
2019-09-19 22:42:45 +00:00
int nLength = ksqrt(dX*dX+dY*dY);
if (nLength <= 0)
nLength = 1;
*pX = DivScale(dX, nLength, 16);
*pY = DivScale(dY, nLength, 16);
2019-09-19 22:42:45 +00:00
}
bool IntersectRay(int wx, int wy, int wdx, int wdy, int x1, int y1, int z1, int x2, int y2, int z2, int *ix, int *iy, int *iz)
{
int dX = x1 - x2;
int dY = y1 - y2;
int dZ = z1 - z2;
int side = wdx * dY - wdy * dX;
int dX2 = x1 - wx;
int dY2 = y1 - wy;
int check1 = dX2 * dY - dY2 * dX;
int check2 = wdx * dY2 - wdy * dX2;
if (side >= 0)
{
if (!side)
return 0;
if (check1 < 0)
return 0;
if (check2 < 0 || check2 >= side)
return 0;
}
else
{
if (check1 > 0)
return 0;
if (check2 > 0 || check2 <= side)
return 0;
}
int nScale = DivScale(check2, side, 16);
*ix = x1 + MulScale(dX, nScale, 16);
*iy = y1 + MulScale(dY, nScale, 16);
*iz = z1 + MulScale(dZ, nScale, 16);
2019-09-19 22:42:45 +00:00
return 1;
}
2021-09-04 18:09:56 +00:00
int HitScan(DBloodActor *actor, int z, int dx, int dy, int dz, unsigned int nMask, int nRange)
2019-09-19 22:42:45 +00:00
{
2021-09-04 18:09:56 +00:00
assert(actor != NULL);
auto pSprite = &actor->s();
assert(dx != 0 || dy != 0);
gHitInfo.clearObj();
2019-09-19 22:42:45 +00:00
int x = pSprite->x;
int y = pSprite->y;
2021-12-18 19:07:47 +00:00
auto bakCstat = pSprite->cstat;
pSprite->cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN;
if (nRange)
2019-09-19 22:42:45 +00:00
{
2021-12-22 09:26:51 +00:00
hitscangoal.X = x + MulScale(nRange << 4, Cos(pSprite->ang), 30);
hitscangoal.y = y + MulScale(nRange << 4, Sin(pSprite->ang), 30);
2019-09-19 22:42:45 +00:00
}
else
{
2021-12-22 09:26:51 +00:00
hitscangoal.X = hitscangoal.y = 0x1ffffff;
2019-09-19 22:42:45 +00:00
}
hitscan({ x, y, z }, pSprite->sector(), { dx, dy, dz << 4 }, gHitInfo, nMask);
2021-12-22 09:26:51 +00:00
hitscangoal.X = hitscangoal.y = 0x1ffffff;
2019-09-19 22:42:45 +00:00
pSprite->cstat = bakCstat;
if (gHitInfo.actor() != nullptr)
2019-09-19 22:42:45 +00:00
return 3;
if (gHitInfo.hitWall != nullptr)
2019-09-19 22:42:45 +00:00
{
auto pWall = gHitInfo.hitWall;
if (!pWall->twoSided())
2019-09-19 22:42:45 +00:00
return 0;
int nZCeil, nZFloor;
getzsofslopeptr(pWall->nextSector(), gHitInfo.hitpos.x, gHitInfo.hitpos.y, &nZCeil, &nZFloor);
if (gHitInfo.hitpos.z <= nZCeil || gHitInfo.hitpos.z >= nZFloor)
2019-09-19 22:42:45 +00:00
return 0;
return 4;
}
if (gHitInfo.hitSector != nullptr)
return 1 + (z < gHitInfo.hitpos.z);
2019-09-19 22:42:45 +00:00
return -1;
}
2021-09-04 18:12:05 +00:00
int VectorScan(DBloodActor *actor, int nOffset, int nZOffset, int dx, int dy, int dz, int nRange, int ac)
2019-09-19 22:42:45 +00:00
{
2021-09-04 18:12:05 +00:00
assert(actor != NULL);
auto pSprite = &actor->s();
2019-09-19 22:42:45 +00:00
int nNum = 256;
assert(pSprite != NULL);
gHitInfo.clearObj();
int x1 = pSprite->x+MulScale(nOffset, Cos(pSprite->ang+512), 30);
int y1 = pSprite->y+MulScale(nOffset, Sin(pSprite->ang+512), 30);
2019-09-19 22:42:45 +00:00
int z1 = pSprite->z+nZOffset;
2021-12-18 19:07:47 +00:00
auto bakCstat = pSprite->cstat;
pSprite->cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN;
2019-09-19 22:42:45 +00:00
if (nRange)
{
2021-12-22 09:26:51 +00:00
hitscangoal.X = x1+MulScale(nRange<<4, Cos(pSprite->ang), 30);
hitscangoal.y = y1+MulScale(nRange<<4, Sin(pSprite->ang), 30);
2019-09-19 22:42:45 +00:00
}
else
{
2021-12-22 09:26:51 +00:00
hitscangoal.X = hitscangoal.y = 0x1fffffff;
2019-09-19 22:42:45 +00:00
}
vec3_t pos = { x1, y1, z1 };
hitscan(pos, pSprite->sector(), { dx, dy, dz << 4 }, gHitInfo, CLIPMASK1);
2021-12-22 09:26:51 +00:00
hitscangoal.X = hitscangoal.y = 0x1ffffff;
2019-09-19 22:42:45 +00:00
pSprite->cstat = bakCstat;
while (nNum--)
{
if (nRange && approxDist(gHitInfo.hitpos.x - pSprite->x, gHitInfo.hitpos.y - pSprite->y) > nRange)
2019-09-19 22:42:45 +00:00
return -1;
if (gHitInfo.actor() != nullptr)
2019-09-19 22:42:45 +00:00
{
spritetype *pOther = &gHitInfo.actor()->s();
if ((pOther->flags & 8) && !(ac & 1))
2019-09-19 22:42:45 +00:00
return 3;
2021-12-18 19:07:47 +00:00
if ((pOther->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) != 0)
2019-09-19 22:42:45 +00:00
return 3;
int nPicnum = pOther->picnum;
2020-05-24 10:31:38 +00:00
if (tileWidth(nPicnum) == 0 || tileHeight(nPicnum) == 0)
2019-09-19 22:42:45 +00:00
return 3;
2020-05-24 10:31:38 +00:00
int height = (tileHeight(nPicnum)*pOther->yrepeat)<<2;
2019-09-19 22:42:45 +00:00
int otherZ = pOther->z;
2021-12-18 19:07:47 +00:00
if (pOther->cstat & CSTAT_SPRITE_YCENTER)
2019-09-19 22:42:45 +00:00
otherZ += height / 2;
2020-05-24 10:31:38 +00:00
int nOffset = tileTopOffset(nPicnum);
2019-09-19 22:42:45 +00:00
if (nOffset)
otherZ -= (nOffset*pOther->yrepeat)<<2;
assert(height > 0);
int height2 = scale(otherZ-gHitInfo.hitpos.z, tileHeight(nPicnum), height);
if (!(pOther->cstat & CSTAT_SPRITE_YFLIP))
2020-05-24 10:31:38 +00:00
height2 = tileHeight(nPicnum)-height2;
if (height2 >= 0 && height2 < tileHeight(nPicnum))
2019-09-19 22:42:45 +00:00
{
2020-05-24 10:31:38 +00:00
int width = (tileWidth(nPicnum)*pOther->xrepeat)>>2;
2019-09-19 22:42:45 +00:00
width = (width*3)/4;
int check1 = ((y1 - pOther->y)*dx - (x1 - pOther->x)*dy) / ksqrt(dx*dx+dy*dy);
assert(width > 0);
2020-05-24 10:31:38 +00:00
int width2 = scale(check1, tileWidth(nPicnum), width);
int nOffset = tileLeftOffset(nPicnum);
2020-05-24 10:31:38 +00:00
width2 += nOffset + tileWidth(nPicnum) / 2;
if (width2 >= 0 && width2 < tileWidth(nPicnum))
2019-09-19 22:42:45 +00:00
{
auto pData = tilePtr(nPicnum);
2020-05-24 10:31:38 +00:00
if (pData[width2*tileHeight(nPicnum)+height2] != TRANSPARENT_INDEX)
2019-09-19 22:42:45 +00:00
return 3;
}
}
2021-12-18 19:07:47 +00:00
auto bakCstat = pOther->cstat;
pOther->cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN;
gHitInfo.clearObj();
pos = gHitInfo.hitpos; // must make a copy!
hitscan(pos, pOther->sector(), { dx, dy, dz << 4 }, gHitInfo, CLIPMASK1);
2019-09-19 22:42:45 +00:00
pOther->cstat = bakCstat;
continue;
}
if (gHitInfo.hitWall != nullptr)
2019-09-19 22:42:45 +00:00
{
walltype *pWall = gHitInfo.hitWall;
if (!pWall->twoSided())
2019-09-19 22:42:45 +00:00
return 0;
sectortype *pSector = gHitInfo.hitSector;
sectortype *pSectorNext = pWall->nextSector();
2019-09-19 22:42:45 +00:00
int nZCeil, nZFloor;
getzsofslopeptr(pWall->nextSector(), gHitInfo.hitpos.x, gHitInfo.hitpos.y, &nZCeil, &nZFloor);
if (gHitInfo.hitpos.z <= nZCeil)
2019-09-19 22:42:45 +00:00
return 0;
if (gHitInfo.hitpos.z >= nZFloor)
2019-09-19 22:42:45 +00:00
{
if (!(pSector->floorstat & CSTAT_SECTOR_SKY) || !(pSectorNext->floorstat & CSTAT_SECTOR_SKY))
2019-09-19 22:42:45 +00:00
return 0;
return 2;
}
2021-12-18 14:18:50 +00:00
if (!(pWall->cstat & (CSTAT_WALL_MASKED | CSTAT_WALL_1WAY)))
2019-09-19 22:42:45 +00:00
return 0;
int nOffset;
2021-12-18 14:18:50 +00:00
if (pWall->cstat & CSTAT_WALL_ALIGN_BOTTOM)
2019-09-19 22:42:45 +00:00
nOffset = ClipHigh(pSector->floorz, pSectorNext->floorz);
else
nOffset = ClipLow(pSector->ceilingz, pSectorNext->ceilingz);
nOffset = (gHitInfo.hitpos.z - nOffset) >> 8;
2021-12-18 14:18:50 +00:00
if (pWall->cstat & CSTAT_WALL_YFLIP)
2019-09-19 22:42:45 +00:00
nOffset = -nOffset;
int nPicnum = pWall->overpicnum;
2020-05-24 10:31:38 +00:00
int nSizX = tileWidth(nPicnum);
int nSizY = tileHeight(nPicnum);
2019-09-19 22:42:45 +00:00
if (!nSizX || !nSizY)
return 0;
nOffset = (nOffset*pWall->yrepeat) / 8;
2020-11-26 07:38:59 +00:00
nOffset += int((nSizY*pWall->ypan_) / 256);
int nLength = approxDist(pWall->x - pWall->point2Wall()->x, pWall->y - pWall->point2Wall()->y);
2019-09-19 22:42:45 +00:00
int nHOffset;
2021-12-18 14:18:50 +00:00
if (pWall->cstat & CSTAT_WALL_XFLIP)
nHOffset = approxDist(gHitInfo.hitpos.x - pWall->point2Wall()->x, gHitInfo.hitpos.y - pWall->point2Wall()->y);
2019-09-19 22:42:45 +00:00
else
nHOffset = approxDist(gHitInfo.hitpos.x - pWall->x, gHitInfo.hitpos.y - pWall->y);
2019-09-19 22:42:45 +00:00
2020-11-26 07:38:59 +00:00
nHOffset = pWall->xpan() + ((nHOffset*pWall->xrepeat) << 3) / nLength;
nHOffset %= nSizX;
nOffset %= nSizY;
auto pData = tilePtr(nPicnum);
2019-09-19 22:42:45 +00:00
int nPixel;
nPixel = nHOffset*nSizY + nOffset;
2019-09-19 22:42:45 +00:00
2020-04-11 22:04:02 +00:00
if (pData[nPixel] == TRANSPARENT_INDEX)
2019-09-19 22:42:45 +00:00
{
2021-12-18 14:18:50 +00:00
auto bakCstat = pWall->cstat;
pWall->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN;
auto bakCstat2 = pWall->nextWall()->cstat;
pWall->nextWall()->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN;
gHitInfo.clearObj();
pos = gHitInfo.hitpos;
hitscan(pos, pWall->nextSector(), { dx, dy, dz << 4 }, gHitInfo, CLIPMASK1);
2019-09-19 22:42:45 +00:00
pWall->cstat = bakCstat;
2021-11-23 18:54:59 +00:00
pWall->nextWall()->cstat = bakCstat2;
2019-09-19 22:42:45 +00:00
continue;
}
return 4;
}
if (gHitInfo.hitSector != nullptr)
2019-09-19 22:42:45 +00:00
{
if (dz > 0)
{
auto actor = barrier_cast<DBloodActor*>(gHitInfo.hitSector->upperLink);
if (!actor) return 2;
auto link = actor->GetOwner();
gHitInfo.clearObj();
x1 = gHitInfo.hitpos.x + link->spr.x - actor->spr.x;
y1 = gHitInfo.hitpos.y + link->spr.y - actor->spr.y;
z1 = gHitInfo.hitpos.z + link->spr.z - actor->spr.z;
pos = { x1, y1, z1 };
hitscan(pos, link->spr.sector(), { dx, dy, dz << 4 }, gHitInfo, CLIPMASK1);
2019-09-19 22:42:45 +00:00
continue;
}
else
{
auto actor = barrier_cast<DBloodActor*>(gHitInfo.hitSector->lowerLink);
if (!actor) return 1;
auto link = actor->GetOwner();
gHitInfo.clearObj();
x1 = gHitInfo.hitpos.x + link->spr.x - actor->spr.x;
y1 = gHitInfo.hitpos.y + link->spr.y - actor->spr.y;
z1 = gHitInfo.hitpos.z + link->spr.z - actor->spr.z;
pos = { x1, y1, z1 };
hitscan(pos, link->spr.sector(), { dx, dy, dz << 4 }, gHitInfo, CLIPMASK1);
2019-09-19 22:42:45 +00:00
continue;
}
}
return -1;
}
return -1;
}
2021-09-04 18:19:38 +00:00
void GetZRange(DBloodActor *actor, int *ceilZ, Collision *ceilColl, int *floorZ, Collision *floorColl, int nDist, unsigned int nMask, unsigned int nClipParallax)
2019-09-19 22:42:45 +00:00
{
2021-09-04 18:19:38 +00:00
assert(actor != NULL);
auto pSprite = &actor->s();
Collision scratch;
2021-09-04 18:19:38 +00:00
2021-12-18 19:07:47 +00:00
auto bakCstat = pSprite->cstat;
int32_t nTemp1;
pSprite->cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
getzrange(pSprite->pos, pSprite->sector(), (int32_t*)ceilZ, *ceilColl, (int32_t*)floorZ, *floorColl, nDist, nMask);
if (floorColl->type == kHitSector)
2019-09-19 22:42:45 +00:00
{
auto pSector = floorColl->hitSector;
if ((nClipParallax & PARALLAXCLIP_FLOOR) == 0 && (pSector->floorstat & CSTAT_SECTOR_SKY))
2019-09-19 22:42:45 +00:00
*floorZ = 0x7fffffff;
2021-11-24 00:11:49 +00:00
if (pSector->hasX())
2019-09-19 22:42:45 +00:00
{
2021-11-24 00:11:49 +00:00
XSECTOR *pXSector = &pSector->xs();
2019-09-19 22:42:45 +00:00
*floorZ += pXSector->Depth << 10;
}
auto actor = barrier_cast<DBloodActor*>(pSector->upperLink);
if (actor)
2019-09-19 22:42:45 +00:00
{
auto link = actor->GetOwner();
vec3_t lpos = pSprite->pos + link->spr.pos - actor->spr.pos;
getzrange(lpos, link->spr.sector(), &nTemp1, scratch, (int32_t*)floorZ, *floorColl, nDist, nMask);
*floorZ -= link->spr.z - actor->spr.z;
2019-09-19 22:42:45 +00:00
}
}
if (ceilColl->type == kHitSector)
2019-09-19 22:42:45 +00:00
{
auto pSector = ceilColl->hitSector;
if ((nClipParallax & PARALLAXCLIP_CEILING) == 0 && (pSector->ceilingstat & CSTAT_SECTOR_SKY))
2019-09-19 22:42:45 +00:00
*ceilZ = 0x80000000;
auto actor = barrier_cast<DBloodActor*>(pSector->lowerLink);
if (actor)
2019-09-19 22:42:45 +00:00
{
auto link = actor->GetOwner();
vec3_t lpos = pSprite->pos + link->spr.pos - actor->spr.pos;
getzrange(lpos, link->spr.sector(), (int32_t*)ceilZ, *ceilColl, &nTemp1, scratch, nDist, nMask);
*ceilZ -= link->spr.z - actor->spr.z;
2019-09-19 22:42:45 +00:00
}
}
pSprite->cstat = bakCstat;
}
2021-11-23 23:21:08 +00:00
void GetZRangeAtXYZ(int x, int y, int z, sectortype* pSector, int *ceilZ, Collision* ceilColl, int* floorZ, Collision* floorColl, int nDist, unsigned int nMask, unsigned int nClipParallax)
2019-09-19 22:42:45 +00:00
{
Collision scratch;
int32_t nTemp1;
vec3_t lpos = { x, y, z };
getzrange(lpos, pSector, (int32_t*)ceilZ, *ceilColl, (int32_t*)floorZ, *floorColl, nDist, nMask);
if (floorColl->type == kHitSector)
2019-09-19 22:42:45 +00:00
{
auto pSector = floorColl->hitSector;
if ((nClipParallax & PARALLAXCLIP_FLOOR) == 0 && (pSector->floorstat & CSTAT_SECTOR_SKY))
2019-09-19 22:42:45 +00:00
*floorZ = 0x7fffffff;
2021-11-23 23:21:08 +00:00
if (pSector->hasX())
2019-09-19 22:42:45 +00:00
{
2021-11-23 23:21:08 +00:00
XSECTOR* pXSector = &pSector->xs();
2019-09-19 22:42:45 +00:00
*floorZ += pXSector->Depth << 10;
}
auto actor = barrier_cast<DBloodActor*>(pSector->upperLink);
if (actor)
2019-09-19 22:42:45 +00:00
{
auto link = actor->GetOwner();
vec3_t newpos = lpos + link->spr.pos - actor->spr.pos;
getzrange(newpos, link->spr.sector(), &nTemp1, scratch, (int32_t*)floorZ, *floorColl, nDist, nMask);
*floorZ -= link->spr.z - actor->spr.z;
2019-09-19 22:42:45 +00:00
}
}
if (ceilColl->type == kHitSector)
2019-09-19 22:42:45 +00:00
{
auto pSector = ceilColl->hitSector;
if ((nClipParallax & PARALLAXCLIP_CEILING) == 0 && (pSector->ceilingstat & CSTAT_SECTOR_SKY))
2019-09-19 22:42:45 +00:00
*ceilZ = 0x80000000;
auto actor = barrier_cast<DBloodActor*>(pSector->lowerLink);
if (actor)
2019-09-19 22:42:45 +00:00
{
auto link = actor->GetOwner();
vec3_t newpos = lpos + link->spr.pos - actor->spr.pos;
getzrange(newpos, link->spr.sector(), (int32_t*)ceilZ, *ceilColl, &nTemp1, scratch, nDist, nMask);
*ceilZ -= link->spr.z - actor->spr.z;
2019-09-19 22:42:45 +00:00
}
}
}
int GetDistToLine(int x1, int y1, int x2, int y2, int x3, int y3)
{
int check = (y1-y3)*(x3-x2);
int check2 = (x1-x2)*(y3-y2);
if (check2 > check)
return -1;
int v8 = DMulScale(x1-x2,x3-x2,y1-y3,y3-y2,4);
int vv = DMulScale(x3-x2,x3-x2,y3-y2,y3-y2,4);
2019-09-19 22:42:45 +00:00
int t1, t2;
if (v8 <= 0)
{
t1 = x2;
t2 = y2;
}
else if (vv > v8)
{
t1 = x2+scale(x3-x2,v8,vv);
t2 = y2+scale(y3-y2,v8,vv);
}
else
{
t1 = x3;
t2 = y3;
}
return approxDist(t1-x1, t2-y1);
}
void ClipMove(vec3_t& pos, sectortype** pSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask, CollisionBase& hit, int tracecount)
2021-11-23 23:16:02 +00:00
{
auto opos = pos;
2021-11-23 23:16:02 +00:00
sectortype* bakSect = *pSector;
clipmove(pos, &bakSect, xv << 14, yv << 14, wd, cd, fd, nMask, hit, tracecount);
2021-11-23 23:16:02 +00:00
if (bakSect == nullptr)
{
pos = opos;
2021-11-23 23:16:02 +00:00
}
else
{
*pSector = bakSect;
}
}
2021-11-23 23:47:05 +00:00
BitArray GetClosestSpriteSectors(sectortype* pSector, int x, int y, int nDist, TArray<walltype*>* pWalls, bool newSectCheckMethod)
2019-09-19 22:42:45 +00:00
{
// by default this function fails with sectors that linked with wide spans, or there was more than one link to the same sector. for example...
// E6M1: throwing TNT on the stone footpath while standing on the brown road will fail due to the start/end points of the span being too far away. it'll only do damage at one end of the road
// E1M2: throwing TNT at the double doors while standing on the train platform
// by setting newSectCheckMethod to true these issues will be resolved
2021-12-21 09:51:41 +00:00
BitArray sectorMap(sector.Size()); // this gets returned to the caller.
sectorMap.Zero();
2021-11-23 23:47:05 +00:00
sectorMap.Set(sectnum(pSector));
double nDist4sq = 256. * nDist * nDist; // (nDist * 16)^2 - * 16 to account for Build's 28.4 fixed point format.
2021-11-23 23:47:05 +00:00
BFSSectorSearch search(pSector);
while (auto pCurSector = search.GetNext())
2019-09-19 22:42:45 +00:00
{
for (auto& wal : wallsofsector(pCurSector))
2019-09-19 22:42:45 +00:00
{
if (!wal.twoSided()) continue;
const auto pNextSector = wal.nextSector();
bool withinRange = false;
if (!newSectCheckMethod) // original method
{
if (search.Check(pNextSector)) // if we've already checked this sector, skip. This is bad, therefore only in compat mode.
continue;
2021-11-21 16:10:38 +00:00
withinRange = CheckProximityWall(wal.point2Wall(), x, y, nDist);
}
else // new method using proper math and no bad shortcut.
{
double dist1 = SquareDistToWall(x, y, &wal);
withinRange = dist1 <= nDist4sq;
}
if (withinRange) // if new sector is within range, add it to the processing queue
2019-09-19 22:42:45 +00:00
{
sectorMap.Set(sectnum(pNextSector));
search.Add(pNextSector);
if (pWalls && wal.hasX())
2019-09-19 22:42:45 +00:00
{
2021-11-19 00:13:33 +00:00
XWALL* pXWall = &wal.xw();
2019-09-19 22:42:45 +00:00
if (pXWall->triggerVector && !pXWall->isTriggered && !pXWall->state)
pWalls->Push(&wal);
2019-09-19 22:42:45 +00:00
}
}
}
}
return sectorMap;
2019-09-19 22:42:45 +00:00
}
int picWidth(int nPic, int repeat) {
return ClipLow((tileWidth(nPic) * repeat) << 2, 0);
}
int picHeight(int nPic, int repeat) {
return ClipLow((tileHeight(nPic) * repeat) << 2, 0);
}
END_BLD_NS