diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 784aa899d..96bd5a33b 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -34,12 +34,12 @@ public: store.Clamp(bitpos); } -private: bool Check(unsigned index) const { return !!(store[bitpos + (index >> 5)] & (1 << (index & 31))); } +private: void Set(unsigned index) { store[bitpos + (index >> 5)] |= (1 << (index & 31)); @@ -250,3 +250,4 @@ inline int sectnum(sectortype* sect) { return int(sect - sector); } + diff --git a/source/games/blood/src/actor.cpp b/source/games/blood/src/actor.cpp index 881d74e4b..0f572910a 100644 --- a/source/games/blood/src/actor.cpp +++ b/source/games/blood/src/actor.cpp @@ -5897,6 +5897,7 @@ static void actCheckProjectiles() // // //--------------------------------------------------------------------------- +static TArray affectedXWalls; // keep this outside the function so that it only needs to be allocated once static void actCheckExplosion() { @@ -5932,15 +5933,12 @@ static void actCheckExplosion() // GetClosestSpriteSectors() has issues checking some sectors due to optimizations // the new flag newSectCheckMethod for GetClosestSpriteSectors() does rectify these issues, but this may cause unintended side effects for level scripted explosions // so only allow this new checking method for dude spawned explosions - short gAffectedXWalls[kMaxXWalls]; + affectedXWalls.Clear(); const bool newSectCheckMethod = !cl_bloodvanillaexplosions && Owner && Owner->IsDudeActor() && !VanillaMode(); // use new sector checking logic - auto sectorMap = GetClosestSpriteSectors(nSector, x, y, radius, gAffectedXWalls, newSectCheckMethod); + auto sectorMap = GetClosestSpriteSectors(nSector, x, y, radius, &affectedXWalls, newSectCheckMethod); - for (int i = 0; i < kMaxXWalls; i++) + for (auto& nWall : affectedXWalls) { - int nWall = gAffectedXWalls[i]; - if (nWall == -1) - break; XWALL* pXWall = &xwall[wall[nWall].extra]; trTriggerWall(nWall, pXWall, kCmdWallImpact); } diff --git a/source/games/blood/src/gameutil.cpp b/source/games/blood/src/gameutil.cpp index 107f4d096..c66a6c728 100644 --- a/source/games/blood/src/gameutil.cpp +++ b/source/games/blood/src/gameutil.cpp @@ -751,62 +751,43 @@ unsigned int ClipMove(vec3_t *pos, int *nSector, int xv, int yv, int wd, int cd, return nRes; } -BitArray GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pWalls, bool newSectCheckMethod) +BitArray GetClosestSpriteSectors(int nSector, int x, int y, int nDist, TArray* pWalls, bool newSectCheckMethod) { // 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 - BitArray sectorMap(numsectors * 2); // first half gets returned to caller, second half is internal work space. + BitArray sectorMap(numsectors); // this gets returned to the caller. sectorMap.Zero(); - - unsigned sectorstart = GlobalSectorList.Size(); - unsigned i = sectorstart; - - GlobalSectorList.Push(nSector); - sectorMap.Set(numsectors + nSector); sectorMap.Set(nSector); - int m = 0; - while (i < GlobalSectorList.Size()) // scan through sectors + BFSSearch search(numsectors, nSector); + + for (unsigned nCurSector; (nCurSector = search.GetNext()) != BFSSearch::EOL;) { - const int nCurSector = GlobalSectorList[i]; - const int nStartWall = sector[nCurSector].wallptr; - const int nEndWall = nStartWall + sector[nCurSector].wallnum; - for (int j = nStartWall; j < nEndWall; j++) // scan each wall of current sector for new sectors + for (auto& wal : wallsofsector(nCurSector)) { - const walltype *pWall = &wall[j]; - const int nNextSector = pWall->nextsector; + const int nNextSector = wal.nextsector; if (nNextSector < 0) // if next wall isn't linked to a sector, skip continue; - if (sectorMap[numsectors + nNextSector]) // if we've already checked this sector, skip - continue; - bool setSectBit = true; bool withinRange = false; if (!newSectCheckMethod) // original method { - withinRange = CheckProximityWall(pWall->point2, x, y, nDist); + if (search.Check(nNextSector)) // if we've already checked this sector, skip. This is bad, therefore only in compat mode. + continue; + withinRange = CheckProximityWall(wal.point2, x, y, nDist); } - else // new method - first test edges and then wall span midpoints + else // new method { - for (int k = (j+1); k < nEndWall; k++) // scan through the rest of the sector's walls - { - if (wall[k].nextsector == nNextSector) // if the next walls still reference the sector, then don't flag the sector as checked (yet) - { - setSectBit = false; - break; - } - } - const int nWallA = j; - const int nWallB = wall[nWallA].point2; - int x1 = wall[nWallA].x, y1 = wall[nWallA].y; - int x2 = wall[nWallB].x, y2 = wall[nWallB].y; - int point1Dist = approxDist(x-x1, y-y1); // setup edge distance needed for below loop (determines which point to shift closer to center) - int point2Dist = approxDist(x-x2, y-y2); - int nLength = approxDist(x1-x2, y1-y2); - const int nDist4 = nDist<<4; - nLength = ClipRange(nLength / (nDist4+(nDist4>>1)), 1, 4); // always test midpoint at least once, and never split more than 4 times + // Q: do this with proper math? + int x1 = wal.x, y1 = wal.y; + int x2 = wal.point2Wall()->x, y2 = wal.point2Wall()->y; + int point1Dist = approxDist(x - x1, y - y1); // setup edge distance needed for below loop (determines which point to shift closer to center) + int point2Dist = approxDist(x - x2, y - y2); + int nLength = approxDist(x1 - x2, y1 - y2); + const int nDist4 = nDist << 4; + nLength = ClipRange(nLength / (nDist4 + (nDist4 >> 1)), 1, 4); // always test midpoint at least once, and never split more than 4 times for (int k = 0; true; k++) // check both points of wall and subdivide span into smaller chunks towards target { withinRange = (point1Dist < nDist4) || (point2Dist < nDist4); // check if both points of span is within radius @@ -814,39 +795,32 @@ BitArray GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pW break; if (k == nLength) // reached end break; - const int xcenter = (x1+x2)>>1, ycenter = (y1+y2)>>1; + const int xcenter = (x1 + x2) >> 1, ycenter = (y1 + y2) >> 1; if (point1Dist < point2Dist) // shift closest side of wall towards target point, and refresh point distance values { x2 = xcenter, y2 = ycenter; - point2Dist = approxDist(x-x2, y-y2); + point2Dist = approxDist(x - x2, y - y2); } else { x1 = xcenter, y1 = ycenter; - point1Dist = approxDist(x-x1, y-y1); + point1Dist = approxDist(x - x1, y - y1); } } } - if (withinRange) // if new sector is within range, set to current sector and test walls + if (withinRange) // if new sector is within range, add it to the processing queue { - setSectBit = true; // sector is within range, set the sector as checked sectorMap.Set(nNextSector); - GlobalSectorList.Push(nNextSector); - if (pWalls && pWall->extra > 0) + search.Add(nNextSector); + if (pWalls && wal.extra > 0) { - XWALL *pXWall = &xwall[pWall->extra]; + XWALL *pXWall = &xwall[wal.extra]; if (pXWall->triggerVector && !pXWall->isTriggered && !pXWall->state) - pWalls[m++] = j; + pWalls->Push(wallnum(&wal)); } } - if (setSectBit) - sectorMap.Set(numsectors + nNextSector); } - i++; } - GlobalSectorList.Resize(sectorstart); - if (pWalls) pWalls[m] = -1; - sectorMap.Resize(numsectors); return sectorMap; } diff --git a/source/games/blood/src/gameutil.h b/source/games/blood/src/gameutil.h index 6e6b53bae..727dfee1f 100644 --- a/source/games/blood/src/gameutil.h +++ b/source/games/blood/src/gameutil.h @@ -84,7 +84,7 @@ void GetZRange(DBloodActor *pSprite, int *ceilZ, Collision *ceilHit, int *floorZ void GetZRangeAtXYZ(int x, int y, int z, int nSector, int *ceilZ, Collision *ceilHit, int *floorZ, Collision *floorHit, int nDist, unsigned int nMask, unsigned int nClipParallax = 0); int GetDistToLine(int x1, int y1, int x2, int y2, int x3, int y3); unsigned int ClipMove(vec3_t* pos, int *nSector, int xv, int yv, int wd, int cd, int fd, unsigned int nMask, int tracecount = 3); -BitArray GetClosestSpriteSectors(int nSector, int x, int y, int nDist, short *pWalls = nullptr, bool newSectCheckMethod = false); +BitArray GetClosestSpriteSectors(int nSector, int x, int y, int nDist, TArray* pWalls, bool newSectCheckMethod = false); int picWidth(int nPic, int repeat); int picHeight(int nPic, int repeat);