diff --git a/source/build/include/build.h b/source/build/include/build.h index eebcf4e11..31b111156 100644 --- a/source/build/include/build.h +++ b/source/build/include/build.h @@ -137,21 +137,7 @@ inline void getzrange(const DVector3& pos, sectortype* sect, double* ceilz, Coll } -extern vec2_t hitscangoal; - struct HitInfoBase; -int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& direction, HitInfoBase& hitinfo, unsigned cliptype); -inline int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& direction, HitInfoBase& hitinfo, unsigned cliptype) -{ - vec3_t istart(int(start.X * worldtoint), int(start.Y * worldtoint), int(start.Z * zworldtoint) ); - vec3_t idir( int(direction.X * worldtoint), int(direction.Y * worldtoint), int(direction.Z * zworldtoint) ); - return hitscan(istart, startsect, idir, hitinfo, cliptype); -} -inline int hitscan(const DVector3& start, const sectortype* startsect, const vec3_t& direction, HitInfoBase& hitinfo, unsigned cliptype) -{ - vec3_t istart(int(start.X * worldtoint), int(start.Y * worldtoint), int(start.Z * zworldtoint)); - return hitscan(istart, startsect, direction, hitinfo, cliptype); -} inline int32_t krand(void) { diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index 90af8b2a8..35e874223 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -1091,190 +1091,3 @@ void getzrange(const vec3_t& pos, sectortype* sect, int32_t* ceilz, CollisionBas } } - -static inline void hit_set(HitInfoBase *hit, sectortype* sect, walltype* wal, DCoreActor* actor, int32_t x, int32_t y, int32_t z) -{ - hit->hitSector = sect; - hit->hitWall = wal; - hit->hitActor = actor; - hit->hitpos.X = x * inttoworld; - hit->hitpos.Y = y * inttoworld; - hit->hitpos.Z = z * zinttoworld; -} - -// stat, heinum, z: either ceiling- or floor- -// how: -1: behave like ceiling, 1: behave like floor -static int32_t hitscan_trysector(const vec3_t *sv, sectortype* sec, HitInfoBase *hit, - int32_t vx, int32_t vy, int32_t vz, - uint16_t stat, int16_t heinum, int32_t z, int32_t how) -{ - int32_t x1 = INT32_MAX, y1 = 0, z1 = 0; - int32_t i; - - if (stat&2) - { - auto const wal = sec->firstWall(); - auto const wal2 = wal->point2Wall(); - int32_t j, dax=wal2->wall_int_pos().X-wal->wall_int_pos().X, day=wal2->wall_int_pos().Y-wal->wall_int_pos().Y; - - i = ksqrt(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue; - i = DivScale(heinum,i, 15); - dax *= i; day *= i; - - j = (vz<<8)-DMulScale(dax,vy,-day,vx, 15); - if (j != 0) - { - i = ((z - sv->Z)<<8)+DMulScale(dax,sv->Y-wal->wall_int_pos().Y,-day,sv->X-wal->wall_int_pos().X, 15); - if (((i^j) >= 0) && ((abs(i)>>1) < abs(j))) - { - i = DivScale(i,j, 30); - x1 = sv->X + MulScale(vx,i, 30); - y1 = sv->Y + MulScale(vy,i, 30); - z1 = sv->Z + MulScale(vz,i, 30); - } - } - } - else if ((how*vz > 0) && (how*sv->Z <= how*z)) - { - z1 = z; i = z1-sv->Z; - if ((abs(i)>>1) < vz*how) - { - i = DivScale(i,vz, 30); - x1 = sv->X + MulScale(vx,i, 30); - y1 = sv->Y + MulScale(vy,i, 30); - } - } - - if ((x1 != INT32_MAX) && (abs(x1-sv->X)+abs(y1-sv->Y) < abs((hit->int_hitpos().X)-sv->X)+abs((hit->int_hitpos().Y)-sv->Y))) - { - if (inside(x1,y1,sec) == 1) - { - hit_set(hit, sec, nullptr, nullptr, x1, y1, z1); - } - } - - return 0; -} - - -// -// hitscan -// - -int hitscan(const vec3_t& start, const sectortype* startsect, const vec3_t& direction, HitInfoBase& hitinfo, unsigned cliptype) -{ - auto const sv = &start; - int const vx = direction.X, vy = direction.Y, vz = direction.Z; - int32_t x1, y1=0, z1=0, x2, y2, intx, inty, intz; - int32_t i, k, daz; - double hitfactor = DBL_MAX; - - const int32_t dawalclipmask = (cliptype&65535); - const int32_t dasprclipmask = (cliptype >> 16); - - hitinfo.clearObj(); // note that this case leaves hitpos untouched. - if (startsect == nullptr) - return -1; - - hitinfo.hitpos.X = hitscangoal.X * inttoworld; - hitinfo.hitpos.Y = hitscangoal.Y * inttoworld; - - BFSSectorSearch search(startsect); - while (auto sec = search.GetNext()) - { - i = 1; - if (hitscan_trysector(sv, sec, &hitinfo, vx,vy,vz, sec->ceilingstat, sec->ceilingheinum, sec->int_ceilingz(), -i)) - continue; - if (hitscan_trysector(sv, sec, &hitinfo, vx,vy,vz, sec->floorstat, sec->floorheinum, sec->int_floorz(), i)) - continue; - - ////////// Walls ////////// - - for(auto& w : wallsofsector(sec)) - { - auto wal = &w; - auto wal2 = wal->point2Wall(); - - auto const nextsect = wal->nextSector(); - - x1 = wal->wall_int_pos().X; y1 = wal->wall_int_pos().Y; x2 = wal2->wall_int_pos().X; y2 = wal2->wall_int_pos().Y; - - if (compat_maybe_truncate_to_int32((coord_t)(x1-sv->X)*(y2-sv->Y)) - < compat_maybe_truncate_to_int32((coord_t)(x2-sv->X)*(y1-sv->Y))) continue; - if (rintersect(sv->X,sv->Y,sv->Z, vx,vy,vz, x1,y1, x2,y2, &intx,&inty,&intz) == -1) continue; - - // temporary crutch - DVector2 vec(vx, vy); - DVector2 newvec(intx - sv->X, inty - sv->Y); - double newfactor = newvec.Sum() / vec.Sum(); - if (newfactor >= hitfactor) continue; - - if ((!wal->twoSided()) || (wal->cstat & EWallFlags::FromInt(dawalclipmask))) - { - hitfactor = newfactor; - hit_set(&hitinfo, sec, wal, nullptr, intx, inty, intz); - continue; - } - - int32_t daz2; - int_getzsofslopeptr(nextsect,intx,inty,&daz,&daz2); - if (intz <= daz || intz >= daz2) - { - hitfactor = newfactor; - hit_set(&hitinfo, sec, wal, nullptr, intx, inty, intz); - continue; - } - - search.Add(nextsect); - } - - ////////// Sprites ////////// - - if (dasprclipmask==0) - continue; - - DVector3 start(sv->X * inttoworld, sv->Y * inttoworld, sv->Z * zinttoworld); - DVector3 vect(vx * inttoworld, vy * inttoworld, vz * zinttoworld); - - TSectIterator it(sec); - while (auto actor = it.Next()) - { - uint32_t const cstat = actor->spr.cstat; - - if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND) - continue; - - if ((cstat&dasprclipmask) == 0) - continue; - - DVector3 v; - double hit = -1; - // we pass hitfactor to the workers because it can shortcut their calculations a lot. - switch (cstat&CSTAT_SPRITE_ALIGNMENT_MASK) - { - case CSTAT_SPRITE_ALIGNMENT_FACING: - hit = intersectSprite(actor, start, vect, v, hitfactor); - break; - - case CSTAT_SPRITE_ALIGNMENT_WALL: - hit = intersectWallSprite(actor, start, vect, v, hitfactor, (picanm[actor->spr.picnum].sf & PICANM_TEXHITSCAN_BIT)); - break; - - case CSTAT_SPRITE_ALIGNMENT_FLOOR: - hit = intersectFloorSprite(actor, start, vect, v, hitfactor); - break; - - case CSTAT_SPRITE_ALIGNMENT_SLOPE: - hit = intersectSlopeSprite(actor, start, vect, v, hitfactor); - break; - } - if (hit > 0) - { - hitfactor = hit; - hitinfo.set(sec, nullptr, actor, v); - } - } - } - - return 0; -} diff --git a/source/core/coreactor.h b/source/core/coreactor.h index 1473808d6..8a1c82afe 100644 --- a/source/core/coreactor.h +++ b/source/core/coreactor.h @@ -228,20 +228,14 @@ extern TArray wall; // Masking these into the object index to keep it in 16 bit was probably the single most dumbest and pointless thing Build ever did. -// Gonna be fun to globally replace these to finally lift the limit this imposes on map size. // Names taken from DukeGDX enum EHitBits { kHitNone = 0, - kHitTypeMask = 0xC000, - kHitTypeMaskSW = 0x1C000, // SW has one more relevant bit - kHitIndexMask = 0x3FFF, kHitSector = 0x4000, kHitWall = 0x8000, kHitSprite = 0xC000, kHitVoid = 0x10000, // SW only - - }; // This serves as input/output for all functions dealing with collisions, hits, etc. diff --git a/source/core/gamefuncs.cpp b/source/core/gamefuncs.cpp index 80ce69e3b..ac71870f7 100644 --- a/source/core/gamefuncs.cpp +++ b/source/core/gamefuncs.cpp @@ -140,7 +140,7 @@ void calcSlope(const sectortype* sec, double xpos, double ypos, double* pceilz, double len = wal->Length(); if (len != 0) { - float fac = (wal->delta().X * (ypos - wal->pos.Y) - wal->delta().Y * (xpos - wal->pos.X)) / len * (1. / SLOPEVAL_FACTOR); + double fac = (wal->delta().X * (ypos - wal->pos.Y) - wal->delta().Y * (xpos - wal->pos.X)) / len * (1. / SLOPEVAL_FACTOR); if (pceilz && sec->ceilingstat & CSTAT_SECTOR_SLOPE) *pceilz += (sec->ceilingheinum * fac); if (pflorz && sec->floorstat & CSTAT_SECTOR_SLOPE) *pflorz += (sec->floorheinum * fac); } @@ -631,9 +631,9 @@ double intersectWallSprite(DCoreActor* actor, const DVector3& start, const DVect return -1; } - double factor2; - double factor = InterceptLineSegments(start.X, start.Y, direction.X, direction.Y, points[0].X, points[0].Y, points[1].X, points[1].Y, &factor2); - if (factor < 0 || factor > maxfactor || factor2 < 0 || factor2 > 1) return -1; + // the wall factor is needed for doing a texture check. + double factor2, factor = InterceptLineSegments(start.X, start.Y, direction.X, direction.Y, points[0].X, points[0].Y, points[1].X, points[1].Y, &factor2); + if (factor < 0 || factor > maxfactor) return -1; result = start + factor * direction; @@ -715,6 +715,182 @@ double intersectSlopeSprite(DCoreActor* actor, const DVector3& start, const DVec return factor; } +//========================================================================== +// +// +// +//========================================================================== + +double checkWallHit(walltype* wal, EWallFlags flagmask, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor) +{ + if (PointOnLineSide(start.XY(), wal) > 0) return -1; + + double factor = InterceptLineSegments(start.X, start.Y, direction.X, direction.Y, wal->pos.X, wal->pos.Y, wal->delta().X, wal->delta().Y); + if (factor <= 0 || factor > maxfactor) return -1; // did not connect. + + result = start + factor * direction; + if (wal->twoSided() && !(wal->cstat & flagmask)) + { + // check if the trace passes this wall or hits the upper or lower tier. + double cz, fz; + getzsofslopeptr(wal->nextSector(), result, &cz, &fz); + if (result.Z > cz && result.Z < fz) return -2; // trace will pass this wall, i.e. no hit. Return -2 to tell the caller to go on. + } + return factor; +} + +//========================================================================== +// +// +// +//========================================================================== + +double checkSectorPlaneHit(sectortype* sec, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor) +{ + if (sec->wallnum < 3) return -1; + auto wal = sec->firstWall(); + double len = wal->Length(); + + double startcz, startfz; + calcSlope(sec, start.X, start.Y, &startcz, &startfz); + + for (int i = 0; i < 2; i++) + { + int heinum; + double factor = -1, z; + bool sloped; + + if (i == 0) + { + if (start.Z <= startcz) continue; + heinum = (sec->ceilingstat & CSTAT_SECTOR_SLOPE) && len > 0 ? sec->ceilingheinum : 0; + z = sec->ceilingz; + } + else + { + if (start.Z >= startfz) continue; + heinum = (sec->floorstat & CSTAT_SECTOR_SLOPE) && len > 0 ? sec->floorheinum : 0; + z = sec->floorz; + } + + if (heinum) + { + auto delta = wal->delta() * heinum * (1. / SLOPEVAL_FACTOR) / len; + + double den = direction.Z - delta.dot(direction.XY().Rotated90CW()); + if (den != 0) + { + auto stdelta = wal->pos - start; + factor = (z - start.Z + delta.dot(stdelta.Rotated90CW())) / den; + } + else continue; + } + else if (direction.Z != 0) + { + factor = (z - start.Z) / direction.Z; + } + if (factor > 0 && factor <= maxfactor) + { + result = start + factor * direction; + return inside(result.X, result.Y, sec); + } + } + + return -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& vect, HitInfoBase& hitinfo, unsigned cliptype, const DVector2* goal) +{ + double hitfactor = DBL_MAX; + + const auto wallflags = EWallFlags::FromInt(cliptype & 65535); + const auto spriteflags = ESpriteFlags::FromInt(cliptype >> 16); + + hitinfo.clearObj(); + if (startsect == nullptr) + return -1; + + if (goal) + { + hitinfo.hitpos.XY() = *goal; + hitfactor = (*goal - start.XY()).Sum() / vect.Sum(); + } + else hitinfo.hitpos.X = hitinfo.hitpos.Y = DBL_MAX; + + BFSSectorSearch search(startsect); + while (auto sec = search.GetNext()) + { + DVector3 v; + double hit = checkSectorPlaneHit(sec, start, vect, v, hitfactor); + if (hit > 0) + { + hitfactor = hit; + hitinfo.set(sec, nullptr, nullptr, v); + } + + // check all walls in this sector + for (auto& w : wallsofsector(sec)) + { + hit = checkWallHit(&w, EWallFlags::FromInt(wallflags), start, vect, v, hitfactor); + if (hit > 0) + { + hitfactor = hit; + hitinfo.set(sec, &w, nullptr, v); + } + else if (hit == -2) + search.Add(w.nextSector()); + } + + if (!spriteflags) + continue; + + //Check all sprites in this sector + TSectIterator it(sec); + while (auto actor = it.Next()) + { + if (actor->spr.cstat2 & CSTAT2_SPRITE_NOFIND) + continue; + + if (!(actor->spr.cstat & spriteflags)) + continue; + + hit = -1; + // we pass hitfactor to the workers because it can shortcut their calculations a lot. + switch (actor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_MASK) + { + case CSTAT_SPRITE_ALIGNMENT_FACING: + hit = intersectSprite(actor, start, vect, v, hitfactor); + break; + + case CSTAT_SPRITE_ALIGNMENT_WALL: + hit = intersectWallSprite(actor, start, vect, v, hitfactor, (picanm[actor->spr.picnum].sf & PICANM_TEXHITSCAN_BIT)); + break; + + case CSTAT_SPRITE_ALIGNMENT_FLOOR: + hit = intersectFloorSprite(actor, start, vect, v, hitfactor); + break; + + case CSTAT_SPRITE_ALIGNMENT_SLOPE: + hit = intersectSlopeSprite(actor, start, vect, v, hitfactor); + break; + } + if (hit > 0) + { + hitfactor = hit; + hitinfo.set(sec, nullptr, actor, v); + } + } + } + + return 0; +} + //========================================================================== // // diff --git a/source/core/gamefuncs.h b/source/core/gamefuncs.h index 56cadbefb..4418247d4 100644 --- a/source/core/gamefuncs.h +++ b/source/core/gamefuncs.h @@ -274,8 +274,11 @@ double intersectSprite(DCoreActor* actor, const DVector3& start, const DVector3& double intersectWallSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor, bool checktex = false); double intersectFloorSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); double intersectSlopeSprite(DCoreActor* actor, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); +double checkWallHit(walltype* wal, EWallFlags flagmask, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); +double checkSectorPlaneHit(sectortype* sec, const DVector3& start, const DVector3& direction, DVector3& result, double maxfactor); void neartag(const DVector3& start, sectortype* sect, DAngle angle, HitInfoBase& result, double neartagrange, int tagsearch); int testpointinquad(const DVector2& pt, const DVector2* quad); +int hitscan(const DVector3& start, const sectortype* startsect, const DVector3& vect, HitInfoBase& hitinfo, unsigned cliptype, const DVector2* goal = nullptr); diff --git a/source/games/blood/src/gameutil.cpp b/source/games/blood/src/gameutil.cpp index 5dd98d3f3..3ebab4659 100644 --- a/source/games/blood/src/gameutil.cpp +++ b/source/games/blood/src/gameutil.cpp @@ -289,22 +289,13 @@ int HitScan(DBloodActor* actor, int z, int dx, int dy, int dz, unsigned int nMas assert(actor != nullptr); assert(dx != 0 || dy != 0); gHitInfo.clearObj(); - int x = actor->int_pos().X; - int y = actor->int_pos().Y; auto bakCstat = actor->spr.cstat; actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN; - if (nRange) - { - hitscangoal.X = x + MulScale(nRange << 4, Cos(actor->int_ang()), 30); - hitscangoal.Y = y + MulScale(nRange << 4, Sin(actor->int_ang()), 30); - } - else - { - hitscangoal.X = hitscangoal.Y = 0x1fffffff; - } - hitscan(vec3_t( x, y, z ), actor->sector(), { dx, dy, dz << 4 }, gHitInfo, nMask); + DVector2 hitscangoal; + if (nRange) hitscangoal = actor->spr.pos.XY() + actor->spr.angle.ToVector() * nRange; + else hitscangoal.Zero(); + hitscan(DVector3(actor->spr.pos.XY(), z * zinttoworld), actor->sector(), DVector3(dx, dy, dz) * inttoworld, gHitInfo, nMask, & hitscangoal); - hitscangoal.X = hitscangoal.Y = 0x1fffffff; actor->spr.cstat = bakCstat; if (gHitInfo.actor() != nullptr) return 3; @@ -340,19 +331,13 @@ int VectorScan(DBloodActor* actor, double nOffset, double nZOffset, const DVecto auto pos = actor->spr.pos.plusZ(nZOffset) + (actor->spr.angle + DAngle90).ToVector() * nOffset; auto bakCstat = actor->spr.cstat; actor->spr.cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN; - if (nRange) - { - auto goal = pos.XY() + actor->spr.angle.ToVector() * nRange; - hitscangoal.X = goal.X * worldtoint; - hitscangoal.Y = goal.Y * worldtoint; - } - else - { - hitscangoal.X = hitscangoal.Y = 0x1fffffff; - } - hitscan(pos, actor->sector(), vel, gHitInfo, CLIPMASK1); - hitscangoal.X = hitscangoal.Y = 0x1ffffff; + DVector2 hitscangoal; + if (nRange) hitscangoal = actor->spr.pos.XY() + actor->spr.angle.ToVector() * nRange; + else hitscangoal.Zero(); + + hitscan(pos, actor->sector(), vel, gHitInfo, CLIPMASK1, &hitscangoal); + actor->spr.cstat = bakCstat; while (nNum--) { diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp index 4636f91ea..3cb62e309 100644 --- a/source/games/duke/src/gameexec.cpp +++ b/source/games/duke/src/gameexec.cpp @@ -2815,7 +2815,7 @@ int ParseState::parse(void) parseifelse( fi.floorspace(g_ac->sector())); break; case concmd_ifnotmoving: - parseifelse( (g_ac->movflag&kHitTypeMask) > kHitSector ); + parseifelse( g_ac->movflag > kHitSector ); break; case concmd_respawnhitag: insptr++;