From 97577dc2d70c2a49a0c8e90eb40c21608a6f1c6e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 5 Mar 2016 21:44:31 +0100 Subject: [PATCH] - added sector portal support to Trace() Note that at the moment only the function itself has been changed, the calling parts still need to be redone. --- src/p_trace.cpp | 1015 +++++++++++++++++++++++++++++------------------ src/p_trace.h | 2 + 2 files changed, 622 insertions(+), 395 deletions(-) diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 297bf52bc..07f89286d 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -41,6 +41,12 @@ #include "r_defs.h" #include "p_spec.h" +//========================================================================== +// +// +// +//========================================================================== + struct FTraceInfo { fixed_t StartX, StartY, StartZ; @@ -49,6 +55,7 @@ struct FTraceInfo DWORD WallMask; AActor *IgnoreThis; FTraceResults *Results; + FTraceResults *TempResults; sector_t *CurSector; fixed_t MaxDist; fixed_t EnterDist; @@ -57,6 +64,8 @@ struct FTraceInfo DWORD TraceFlags; int inshootthrough; fixed_t startfrac; + int aimdir; + fixed_t limitz; // These are required for 3D-floor checking // to create a fake sector with a floor @@ -64,15 +73,37 @@ struct FTraceInfo sector_t DummySector[2]; int sectorsel; + void Setup3DFloors(); + bool LineCheck(intercept_t *in); + bool ThingCheck(intercept_t *in); bool TraceTraverse (int ptflags); bool CheckPlane(const secplane_t &plane); - bool CheckSectorPlane (const sector_t *sector, bool checkFloor); - bool Check3DFloorPlane(const F3DFloor *ffloor, bool checkBottom); + void EnterLinePortal(line_t *li, fixed_t frac); + void EnterSectorPortal(int position, fixed_t frac, sector_t *entersec); + + + bool CheckSectorPlane(const sector_t *sector, bool checkFloor) + { + return CheckPlane(checkFloor ? sector->floorplane : sector->ceilingplane); + } + + bool FTraceInfo::Check3DFloorPlane(const F3DFloor *ffloor, bool checkBottom) + { + return CheckPlane(checkBottom? *(ffloor->bottom.plane) : *(ffloor->top.plane)); + } + + }; static bool EditTraceResult (DWORD flags, FTraceResults &res); +//========================================================================== +// +// Trace entry point +// +//========================================================================== + bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, ActorFlags actorMask, DWORD wallMask, AActor *ignore, @@ -81,6 +112,10 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, { int ptflags; FTraceInfo inf; + FTraceResults tempResult; + + memset(&tempResult, 0, sizeof(tempResult)); + tempResult.Fraction = tempResult.Distance = FIXED_MAX; ptflags = actorMask ? PT_ADDLINES|PT_ADDTHINGS|PT_COMPATIBLE : PT_ADDLINES; @@ -100,8 +135,11 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, inf.TraceCallbackData = callbackdata; inf.TraceFlags = flags; inf.Results = &res; + inf.TempResults = &tempResult; inf.inshootthrough = true; inf.sectorsel=0; + inf.aimdir = -1; + inf.startfrac = 0; memset(&res, 0, sizeof(res)); /* // Redundant with the memset res.HitType = TRACE_HitNone; @@ -133,34 +171,11 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, maxDist = inf.MaxDist = FixedDiv(FIXED_MIN - y, vy); } - // recalculate the trace's end points for robustness if (inf.TraceTraverse (ptflags)) - { // check for intersection with floor/ceiling - res.Sector = §ors[inf.CurSector->sectornum]; - - if (inf.CheckSectorPlane (inf.CurSector, true)) - { - res.HitType = TRACE_HitFloor; - res.HitTexture = inf.CurSector->GetTexture(sector_t::floor); - if (res.CrossedWater == NULL && - inf.CurSector->heightsec != NULL && - inf.CurSector->heightsec->floorplane.ZatPoint (res.X, res.Y) >= res.Z) - { - res.CrossedWater = §ors[inf.CurSector->sectornum]; - } - } - else if (inf.CheckSectorPlane (inf.CurSector, false)) - { - res.HitType = TRACE_HitCeiling; - res.HitTexture = inf.CurSector->GetTexture(sector_t::ceiling); - } - } - - if (res.HitType != TRACE_HitNone) - { + { if (flags) { - return EditTraceResult (flags, res); + return EditTraceResult(flags, res); } else { @@ -179,9 +194,116 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, } } -bool FTraceInfo::TraceTraverse (int ptflags) + +//============================================================================ +// +// traverses a sector portal +// +//============================================================================ + +void FTraceInfo::EnterSectorPortal(int position, fixed_t frac, sector_t *entersec) +{ + if (aimdir != -1 && aimdir != position) return; + AActor *portal = entersec->SkyBoxes[position]; + + if (aimdir == sector_t::ceiling && portal->threshold < limitz) return; + else if (aimdir == sector_t::floor && portal->threshold > limitz) return; + + FTraceInfo newtrace; + FTraceResults results; + + memset(&results, 0, sizeof(results)); + + newtrace.StartX = StartX + portal->scaleX; + newtrace.StartY = StartY + portal->scaleY; + newtrace.StartZ = StartZ; + + frac += FixedDiv(FRACUNIT, MaxDist); + fixed_t enterdist = FixedMul(MaxDist, frac); + fixed_t enterX = newtrace.StartX + FixedMul(enterdist, Vx); + fixed_t enterY = newtrace.StartY + FixedMul(enterdist, Vy); + + newtrace.Vx = Vx; + newtrace.Vy = Vy; + newtrace.Vz = Vz; + newtrace.ActorMask = ActorMask; + newtrace.WallMask = WallMask; + newtrace.IgnoreThis = IgnoreThis; + newtrace.Results = &results; + newtrace.TempResults = TempResults; + newtrace.CurSector = P_PointInSector(enterX ,enterY); + newtrace.MaxDist = MaxDist; + newtrace.EnterDist = EnterDist; + newtrace.TraceCallback = TraceCallback; + newtrace.TraceCallbackData = TraceCallbackData; + newtrace.TraceFlags = TraceFlags; + newtrace.inshootthrough = true; + newtrace.startfrac = frac; + newtrace.aimdir = position; + newtrace.limitz = portal->threshold; + newtrace.sectorsel = 0; + + if (newtrace.TraceTraverse(ActorMask ? PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE : PT_ADDLINES)) + { + if (newtrace.Results->HitType != TRACE_HitNone) + { + if (newtrace.Results->Fraction < TempResults->Fraction) + { + *TempResults = *newtrace.Results; + } + } + } +} + +//============================================================================ +// +// traverses a line portal +// simply calling PortalRelocate does not work here because more needs to be set up +// +//============================================================================ + +void FTraceInfo::EnterLinePortal(line_t *li, fixed_t frac) +{ + /* + aim_t newtrace = Clone(); + + FLinePortal *port = li->getPortal(); + if (port->mType != PORTT_LINKED && (flags & ALF_PORTALRESTRICT)) return; + + newtrace.toppitch = toppitch; + newtrace.bottompitch = bottompitch; + newtrace.aimdir = aimdir; + newtrace.unlinked = (port->mType != PORTT_LINKED); + newtrace.startpos = startpos; + newtrace.aimtrace = aimtrace; + P_TranslatePortalXY(li, newtrace.startpos.x, newtrace.startpos.y); + P_TranslatePortalZ(li, newtrace.startpos.z); + P_TranslatePortalVXVY(li, newtrace.aimtrace.x, newtrace.aimtrace.y); + + newtrace.startfrac = frac + FixedDiv(FRACUNIT, attackrange); // this is to skip the transition line to the portal which would produce a bogus opening + + fixed_t x = newtrace.startpos.x + FixedMul(newtrace.aimtrace.x, newtrace.startfrac); + fixed_t y = newtrace.startpos.y + FixedMul(newtrace.aimtrace.y, newtrace.startfrac); + + newtrace.lastsector = P_PointInSector(x, y); + P_TranslatePortalZ(li, limitz); + if (aimdebug) + Printf("-----Entering line portal from sector %d to sector %d\n", lastsector->sectornum, newtrace.lastsector->sectornum); + newtrace.AimTraverse(); + SetResult(linetarget, newtrace.linetarget); + SetResult(thing_friend, newtrace.thing_friend); + SetResult(thing_other, newtrace.thing_other); + */ +} + +//========================================================================== +// +// Checks 3D floors at trace start and sets up related data +// +//========================================================================== + +void FTraceInfo::Setup3DFloors() { - // Do a 3D floor check in the starting sector TDeletingArray &ff = CurSector->e->XFloor.ffloors; if (ff.Size()) @@ -195,6 +317,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) fixed_t y = StartY + FixedMul(Vy, sdist); fixed_t z = StartZ + FixedMul(Vz, sdist); + fixed_t bf = CurSector->floorplane.ZatPoint(x, y); fixed_t bc = CurSector->ceilingplane.ZatPoint(x, y); @@ -206,7 +329,10 @@ bool FTraceInfo::TraceTraverse (int ptflags) if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL) { if (Check3DFloorPlane(rover, false)) + { Results->Crossed3DWater = rover; + Results->Crossed3DWaterPos = { Results->X, Results->Y, Results->Z }; + } } if (!(rover->flags&FF_SHOOTTHROUGH)) @@ -214,10 +340,10 @@ bool FTraceInfo::TraceTraverse (int ptflags) fixed_t ff_bottom = rover->bottom.plane->ZatPoint(x, y); fixed_t ff_top = rover->top.plane->ZatPoint(x, y); // clip to the part of the sector we are in - if (z>ff_top) + if (z > ff_top) { // above - if (bffloorplane = *rover->top.plane; CurSector->SetTexture(sector_t::floor, *rover->top.texture, false); @@ -225,10 +351,10 @@ bool FTraceInfo::TraceTraverse (int ptflags) bf = ff_top; } } - else if (zff_bottom) + if (bc > ff_bottom) { CurSector->ceilingplane = *rover->bottom.plane; CurSector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); @@ -239,7 +365,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) else { // inside - if (bffloorplane = *rover->bottom.plane; CurSector->SetTexture(sector_t::floor, *rover->bottom.texture, false); @@ -247,7 +373,7 @@ bool FTraceInfo::TraceTraverse (int ptflags) bf = ff_bottom; } - if (bc>ff_top) + if (bc > ff_top) { CurSector->ceilingplane = *rover->top.plane; CurSector->SetTexture(sector_t::ceiling, *rover->top.texture, false); @@ -259,379 +385,381 @@ bool FTraceInfo::TraceTraverse (int ptflags) } } } - - - FPathTraverse it(StartX, StartY, FixedMul (Vx, MaxDist), FixedMul (Vy, MaxDist), ptflags | PT_DELTA); - intercept_t *in; - - while ((in = it.Next())) + if (!CurSector->PortalBlocksMovement(sector_t::ceiling)) { - fixed_t hitx, hity, hitz; - fixed_t dist; + EnterSectorPortal(sector_t::ceiling, startfrac, CurSector); + } + if (!CurSector->PortalBlocksMovement(sector_t::floor)) + { + EnterSectorPortal(sector_t::floor, startfrac, CurSector); + } +} - // Deal with splashes in 3D floors - if (CurSector->e->XFloor.ffloors.Size()) + +//========================================================================== +// +// Processes one line intercept +// +//========================================================================== + +bool FTraceInfo::LineCheck(intercept_t *in) +{ + int lineside; + sector_t *entersector; + + fixed_t dist = FixedMul(MaxDist, in->frac); + fixed_t hitx = StartX + FixedMul(Vx, dist); + fixed_t hity = StartY + FixedMul(Vy, dist); + fixed_t hitz = StartZ + FixedMul(Vz, dist); + + fixed_t ff, fc, bf = 0, bc = 0; + + if (in->d.line->frontsector->sectornum == CurSector->sectornum) + { + lineside = 0; + } + else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) + { + lineside = 1; + } + else + { // Dammit. Why does Doom have to allow non-closed sectors? + if (in->d.line->backsector == NULL) { - for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) - { - F3DFloor * rover=CurSector->e->XFloor.ffloors[i]; - if (!(rover->flags&FF_EXISTS)) - continue; - - // Deal with splashy stuff - if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL) - { - if (Check3DFloorPlane(rover, false)) - Results->Crossed3DWater = rover; - } - } + lineside = 0; + CurSector = in->d.line->frontsector; } - - if (in->isaline) + else { - int lineside; - sector_t *entersector; + lineside = P_PointOnLineSide(StartX, StartY, in->d.line); + CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; + } + } - dist = FixedMul (MaxDist, in->frac); - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); + if (!(in->d.line->flags & ML_TWOSIDED)) + { + entersector = NULL; + } + else + { + entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; - fixed_t ff, fc, bf = 0, bc = 0; - - // CurSector may be a copy so we must compare the sector number, not the index! - if (in->d.line->frontsector->sectornum == CurSector->sectornum) + // For backwards compatibility: Ignore lines with the same sector on both sides. + // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it. + if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) + { + // We must check special activation here because the code below is never reached. + if (TraceFlags & TRACE_PCross) { - lineside = 0; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross); } - else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) + if (TraceFlags & TRACE_Impact) { - lineside = 1; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } - else - { // Dammit. Why does Doom have to allow non-closed sectors? - if (in->d.line->backsector == NULL) + return true; + } + } + + ff = CurSector->floorplane.ZatPoint(hitx, hity); + fc = CurSector->ceilingplane.ZatPoint(hitx, hity); + + if (entersector != NULL) + { + bf = entersector->floorplane.ZatPoint(hitx, hity); + bc = entersector->ceilingplane.ZatPoint(hitx, hity); + } + + sector_t *hsec = CurSector->GetHeightSec(); + if (Results->CrossedWater == NULL && + hsec != NULL && + //CurSector->heightsec->waterzone && + hitz <= hsec->floorplane.ZatPoint(hitx, hity)) + { + // hit crossed a water plane + if (CheckSectorPlane(hsec, true)) + { + Results->CrossedWater = §ors[CurSector->sectornum]; + Results->CrossedWaterPos = { Results->X, Results->Y, Results->Z }; + } + } + + if (hitz <= ff) + { + if (CurSector->PortalBlocksMovement(sector_t::floor)) + { + // hit floor in front of wall + Results->HitType = TRACE_HitFloor; + Results->HitTexture = CurSector->GetTexture(sector_t::floor); + } + else if (entersector == NULL || entersector->PortalBlocksMovement(sector_t::floor)) + { + // hit beyond a portal plane. This needs to be taken care of by the trace spawned on the other side. + Results->HitType = TRACE_HitNone; + return false; + } + } + else if (hitz >= fc) + { + if (CurSector->PortalBlocksMovement(sector_t::ceiling)) + { + // hit ceiling in front of wall + Results->HitType = TRACE_HitCeiling; + Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); + } + else if (entersector == NULL || entersector->PortalBlocksMovement(sector_t::ceiling)) + { + // hit beyond a portal plane. This needs to be taken care of by the trace spawned on the other side. + Results->HitType = TRACE_HitNone; + return false; + } + } + else if (entersector == NULL || + hitz < bf || hitz > bc || + in->d.line->flags & WallMask) + { + // hit the wall + Results->HitType = TRACE_HitWall; + Results->Tier = + entersector == NULL ? TIER_Middle : + hitz <= bf ? TIER_Lower : + hitz >= bc ? TIER_Upper : TIER_Middle; + if (TraceFlags & TRACE_Impact) + { + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); + } + } + else + { // made it past the wall + // check for 3D floors first + if (entersector->e->XFloor.ffloors.Size()) + { + memcpy(&DummySector[sectorsel], entersector, sizeof(sector_t)); + entersector = &DummySector[sectorsel]; + sectorsel ^= 1; + + for (auto rover : entersector->e->XFloor.ffloors) + { + int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH); + + if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS) { - lineside = 0; - CurSector = in->d.line->frontsector; - } - else - { - lineside = P_PointOnLineSide (StartX, StartY, in->d.line); - CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; - } - } + fixed_t ff_bottom = rover->bottom.plane->ZatPoint(hitx, hity); + fixed_t ff_top = rover->top.plane->ZatPoint(hitx, hity); - if (!(in->d.line->flags & ML_TWOSIDED)) - { - entersector = NULL; - } - else - { - entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; - - // For backwards compatibility: Ignore lines with the same sector on both sides. - // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it. - if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) - { - // We must check special activation here because the code below is never reached. - if (TraceFlags & TRACE_PCross) + // clip to the part of the sector we are in + if (hitz > ff_top) { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross); - } - if (TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - continue; - } - } - - ff = CurSector->floorplane.ZatPoint (hitx, hity); - fc = CurSector->ceilingplane.ZatPoint (hitx, hity); - - if (entersector != NULL) - { - bf = entersector->floorplane.ZatPoint (hitx, hity); - bc = entersector->ceilingplane.ZatPoint (hitx, hity); - } - - sector_t *hsec = CurSector->GetHeightSec(); - if (Results->CrossedWater == NULL && - hsec != NULL && - //CurSector->heightsec->waterzone && - hitz <= hsec->floorplane.ZatPoint (hitx, hity)) - { - // hit crossed a water plane - Results->CrossedWater = §ors[CurSector->sectornum]; - } - - if (hitz <= ff) - { // hit floor in front of wall - Results->HitType = TRACE_HitFloor; - Results->HitTexture = CurSector->GetTexture(sector_t::floor); - } - else if (hitz >= fc) - { // hit ceiling in front of wall - Results->HitType = TRACE_HitCeiling; - Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); - } - else if (entersector == NULL || - hitz < bf || hitz > bc || - in->d.line->flags & WallMask) - { // hit the wall - Results->HitType = TRACE_HitWall; - Results->Tier = - entersector == NULL ? TIER_Middle : - hitz <= bf ? TIER_Lower : - hitz >= bc ? TIER_Upper : TIER_Middle; - if (TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - } - else - { // made it past the wall - // check for 3D floors first - if (entersector->e->XFloor.ffloors.Size()) - { - memcpy(&DummySector[sectorsel],entersector,sizeof(sector_t)); - entersector=&DummySector[sectorsel]; - sectorsel^=1; - - for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) - { - F3DFloor * rover=entersector->e->XFloor.ffloors[i]; - int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH); - - if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS) + // above + if (bf < ff_top) { - fixed_t ff_bottom=rover->bottom.plane->ZatPoint(hitx, hity); - fixed_t ff_top=rover->top.plane->ZatPoint(hitx, hity); - - // clip to the part of the sector we are in - if (hitz>ff_top) - { - // above - if (bffloorplane=*rover->top.plane; - entersector->SetTexture(sector_t::floor, *rover->top.texture, false); - bf=ff_top; - } - } - else if (hitzff_bottom) - { - entersector->ceilingplane=*rover->bottom.plane; - entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); - bc=ff_bottom; - } - } - else - { - //hit the edge - equivalent to hitting the wall - Results->HitType = TRACE_HitWall; - Results->Tier = TIER_FFloor; - Results->ffloor = rover; - if ((TraceFlags & TRACE_Impact) && in->d.line->special) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - goto cont; - } + entersector->floorplane = *rover->top.plane; + entersector->SetTexture(sector_t::floor, *rover->top.texture, false); + entersector->SkyBoxes[sector_t::floor] = NULL; + bf = ff_top; } } - } - - Results->HitType = TRACE_HitNone; - if (TraceFlags & TRACE_PCross) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross); - } - if (TraceFlags & TRACE_Impact) - { // This is incorrect for "impact", but Hexen did this, so - // we need to as well, for compatibility - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); - } - } -cont: - - if (Results->HitType != TRACE_HitNone) - { - // We hit something, so figure out where exactly - Results->Sector = §ors[CurSector->sectornum]; - - if (Results->HitType != TRACE_HitWall && - !CheckSectorPlane (CurSector, Results->HitType == TRACE_HitFloor)) - { // trace is parallel to the plane (or right on it) - if (entersector == NULL) + else if (hitz < ff_bottom) { - Results->HitType = TRACE_HitWall; - Results->Tier = TIER_Middle; + //below + if (bc > ff_bottom) + { + entersector->ceilingplane = *rover->bottom.plane; + entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false); + entersector->SkyBoxes[sector_t::ceiling] = NULL; + bc = ff_bottom; + } } else { - if (hitz <= bf || hitz >= bc) + //hit the edge - equivalent to hitting the wall + Results->HitType = TRACE_HitWall; + Results->Tier = TIER_FFloor; + Results->ffloor = rover; + if ((TraceFlags & TRACE_Impact) && in->d.line->special) { - Results->HitType = TRACE_HitWall; - Results->Tier = - hitz <= bf ? TIER_Lower : - hitz >= bc ? TIER_Upper : TIER_Middle; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } - else - { - Results->HitType = TRACE_HitNone; - } - } - if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact); + goto cont; } } - - if (Results->HitType == TRACE_HitWall) - { - Results->X = hitx; - Results->Y = hity; - Results->Z = hitz; - Results->Distance = dist; - Results->Fraction = in->frac; - Results->Line = in->d.line; - Results->Side = lineside; - } } + } - if (Results->HitType == TRACE_HitNone) - { - CurSector = entersector; - EnterDist = dist; - continue; - } + Results->HitType = TRACE_HitNone; + if (TraceFlags & TRACE_PCross) + { + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross); + } + if (TraceFlags & TRACE_Impact) + { // This is incorrect for "impact", but Hexen did this, so + // we need to as well, for compatibility + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); + } - if (TraceCallback != NULL) + if (!entersector->PortalBlocksMovement(sector_t::ceiling)) + { + EnterSectorPortal(sector_t::ceiling, in->frac, entersector); + } + if (!entersector->PortalBlocksMovement(sector_t::floor)) + { + EnterSectorPortal(sector_t::floor, in->frac, entersector); + } + } +cont: + + if (Results->HitType != TRACE_HitNone) + { + // We hit something, so figure out where exactly + Results->Sector = §ors[CurSector->sectornum]; + + if (Results->HitType != TRACE_HitWall && + !CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) + { // trace is parallel to the plane (or right on it) + if (entersector == NULL) { - switch (TraceCallback(*Results, TraceCallbackData)) - { - case TRACE_Stop: return false; - case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; - case TRACE_Skip: Results->HitType = TRACE_HitNone; break; - default: break; - } + Results->HitType = TRACE_HitWall; + Results->Tier = TIER_Middle; } else { - return false; - } - } - - // Encountered an actor - if (!(in->d.thing->flags & ActorMask) || in->d.thing == IgnoreThis) - { - continue; - } - - dist = FixedMul (MaxDist, in->frac); - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - if (hitz > in->d.thing->Top()) - { // trace enters above actor - if (Vz >= 0) continue; // Going up: can't hit - - // Does it hit the top of the actor? - dist = FixedDiv(in->d.thing->Top() - StartZ, Vz); - - if (dist > MaxDist) continue; - in->frac = FixedDiv(dist, MaxDist); - - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - // calculated coordinate is outside the actor's bounding box - if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || - abs(hity - in->d.thing->Y()) > in->d.thing->radius) continue; - } - else if (hitz < in->d.thing->Z()) - { // trace enters below actor - if (Vz <= 0) continue; // Going down: can't hit - - // Does it hit the bottom of the actor? - dist = FixedDiv(in->d.thing->Z() - StartZ, Vz); - if (dist > MaxDist) continue; - in->frac = FixedDiv(dist, MaxDist); - - hitx = StartX + FixedMul (Vx, dist); - hity = StartY + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - // calculated coordinate is outside the actor's bounding box - if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || - abs(hity - in->d.thing->Y()) > in->d.thing->radius) continue; - } - - // check for extrafloors first - if (CurSector->e->XFloor.ffloors.Size()) - { - fixed_t ff_floor=CurSector->floorplane.ZatPoint(hitx, hity); - fixed_t ff_ceiling=CurSector->ceilingplane.ZatPoint(hitx, hity); - - if (hitz>ff_ceiling) // actor is hit above the current ceiling - { - Results->HitType=TRACE_HitCeiling; - Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); - } - else if (hitzHitType=TRACE_HitFloor; - Results->HitTexture = CurSector->GetTexture(sector_t::floor); - } - else goto cont1; - - // the trace hit a 3D-floor before the thing. - // Calculate an intersection and abort. - Results->Sector = §ors[CurSector->sectornum]; - if (!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) - { - Results->HitType = TRACE_HitNone; - } - if (TraceCallback != NULL) - { - switch (TraceCallback(*Results, TraceCallbackData)) + if (hitz <= bf || hitz >= bc) { - case TRACE_Continue: return true; - case TRACE_Stop: return false; - case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; - case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; + Results->HitType = TRACE_HitWall; + Results->Tier = + hitz <= bf ? TIER_Lower : + hitz >= bc ? TIER_Upper : TIER_Middle; + } + else + { + Results->HitType = TRACE_HitNone; } } - else + if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) { - return false; + P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact); } } -cont1: - Results->HitType = TRACE_HitActor; - Results->X = hitx; - Results->Y = hity; - Results->Z = hitz; - Results->Distance = dist; - Results->Fraction = in->frac; - Results->Actor = in->d.thing; + if (Results->HitType == TRACE_HitWall) + { + Results->X = hitx; + Results->Y = hity; + Results->Z = hitz; + Results->Distance = dist; + Results->Fraction = in->frac; + Results->Line = in->d.line; + Results->Side = lineside; + } + } + if (TraceCallback != NULL && Results->HitType != TRACE_HitNone) + { + switch (TraceCallback(*Results, TraceCallbackData)) + { + case TRACE_Stop: return false; + case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; + case TRACE_Skip: Results->HitType = TRACE_HitNone; break; + default: break; + } + } + + if (Results->HitType == TRACE_HitNone) + { + CurSector = entersector; + EnterDist = dist; + return true; + } + else + { + return false; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +bool FTraceInfo::ThingCheck(intercept_t *in) +{ + fixed_t dist = FixedMul(MaxDist, in->frac); + fixed_t hitx = StartX + FixedMul(Vx, dist); + fixed_t hity = StartY + FixedMul(Vy, dist); + fixed_t hitz = StartZ + FixedMul(Vz, dist); + + if (hitz > in->d.thing->Top()) + { + // trace enters above actor + if (Vz >= 0) return true; // Going up: can't hit + + // Does it hit the top of the actor? + dist = FixedDiv(in->d.thing->Top() - StartZ, Vz); + + if (dist > MaxDist) return true; + in->frac = FixedDiv(dist, MaxDist); + + hitx = StartX + FixedMul(Vx, dist); + hity = StartY + FixedMul(Vy, dist); + hitz = StartZ + FixedMul(Vz, dist); + + // calculated coordinate is outside the actor's bounding box + if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || + abs(hity - in->d.thing->Y()) > in->d.thing->radius) return true; + } + else if (hitz < in->d.thing->Z()) + { // trace enters below actor + if (Vz <= 0) return true; // Going down: can't hit + + // Does it hit the bottom of the actor? + dist = FixedDiv(in->d.thing->Z() - StartZ, Vz); + if (dist > MaxDist) return true; + in->frac = FixedDiv(dist, MaxDist); + + hitx = StartX + FixedMul(Vx, dist); + hity = StartY + FixedMul(Vy, dist); + hitz = StartZ + FixedMul(Vz, dist); + + // calculated coordinate is outside the actor's bounding box + if (abs(hitx - in->d.thing->X()) > in->d.thing->radius || + abs(hity - in->d.thing->Y()) > in->d.thing->radius) return true; + } + + if (CurSector->e->XFloor.ffloors.Size()) + { + // check for 3D floor hits first. + fixed_t ff_floor = CurSector->floorplane.ZatPoint(hitx, hity); + fixed_t ff_ceiling = CurSector->ceilingplane.ZatPoint(hitx, hity); + + if (hitz > ff_ceiling && CurSector->PortalBlocksMovement(sector_t::ceiling)) // actor is hit above the current ceiling + { + Results->HitType = TRACE_HitCeiling; + Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); + } + else if (hitz < ff_floor && CurSector->PortalBlocksMovement(sector_t::floor)) // actor is hit below the current floor + { + Results->HitType = TRACE_HitFloor; + Results->HitTexture = CurSector->GetTexture(sector_t::floor); + } + else goto cont1; + + // the trace hit a 3D floor before the thing. + // Calculate an intersection and abort. + Results->Sector = §ors[CurSector->sectornum]; + if (!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor)) + { + Results->HitType = TRACE_HitNone; + } if (TraceCallback != NULL) { switch (TraceCallback(*Results, TraceCallbackData)) { - case TRACE_Stop: return false; - case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; - case TRACE_Skip: Results->HitType = TRACE_HitNone; break; - default: break; + case TRACE_Continue: return true; + case TRACE_Stop: return false; + case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; + case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; } } else @@ -639,9 +767,132 @@ cont1: return false; } } +cont1: + + + Results->HitType = TRACE_HitActor; + Results->X = hitx; + Results->Y = hity; + Results->Z = hitz; + Results->Distance = dist; + Results->Fraction = in->frac; + Results->Actor = in->d.thing; + + if (TraceCallback != NULL) + { + switch (TraceCallback(*Results, TraceCallbackData)) + { + case TRACE_Stop: return false; + case TRACE_Abort: Results->HitType = TRACE_HitNone; return false; + case TRACE_Skip: Results->HitType = TRACE_HitNone; return true; + default: return true; + } + } + else + { + return false; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FTraceInfo::TraceTraverse (int ptflags) +{ + // Do a 3D floor check in the starting sector + Setup3DFloors(); + + FPathTraverse it(StartX, StartY, FixedMul (Vx, MaxDist), FixedMul (Vy, MaxDist), ptflags | PT_DELTA, startfrac); + intercept_t *in; + int lastsplashsector = -1; + + while ((in = it.Next())) + { + // Deal with splashes in 3D floors (but only run once per sector, not each iteration - and stop if something was found.) + if (Results->Crossed3DWater == NULL && lastsplashsector != CurSector->sectornum) + { + for (auto rover : CurSector->e->XFloor.ffloors) + { + if ((rover->flags & FF_EXISTS) && (rover->flags&FF_SWIMMABLE)) + { + if (Check3DFloorPlane(rover, false)) + { + Results->Crossed3DWater = rover; + Results->Crossed3DWaterPos = { Results->X, Results->Y, Results->Z }; + } + } + } + lastsplashsector = CurSector->sectornum; + } + + // We have something closer in the storage for portal subtraces. + if (TempResults->HitType != TRACE_HitNone && in->frac > TempResults->Fraction) + { + break; + } + else if (in->isaline) + { + if (!LineCheck(in)) break; + } + else if ((in->d.thing->flags & ActorMask) && in->d.thing != IgnoreThis) + { + if (!ThingCheck(in)) break; + } + } + + // Check if one subtrace through a portal yielded a better result. + if (TempResults->HitType != TRACE_HitNone && + (Results->HitType == TRACE_HitNone || Results->Fraction > TempResults->Fraction)) + { + *Results = *TempResults; + return true; + } + else if (Results->HitType == TRACE_HitNone) + { + if (CurSector->PortalBlocksMovement(sector_t::floor) && CheckSectorPlane(CurSector, true)) + { + Results->HitType = TRACE_HitFloor; + Results->HitTexture = CurSector->GetTexture(sector_t::floor); + } + else if (CurSector->PortalBlocksMovement(sector_t::ceiling) && CheckSectorPlane(CurSector, false)) + { + Results->HitType = TRACE_HitCeiling; + Results->HitTexture = CurSector->GetTexture(sector_t::ceiling); + } + } + + // check for intersection with floor/ceiling + Results->Sector = §ors[CurSector->sectornum]; + + if (Results->CrossedWater == NULL && + CurSector->heightsec != NULL && + CurSector->heightsec->floorplane.ZatPoint(Results->X, Results->Y) >= Results->Z) + { + // Save the result so that the water check doesn't destroy it. + FTraceResults *res = Results; + FTraceResults hsecResult; + Results = &hsecResult; + + if (CheckSectorPlane(CurSector->heightsec, true)) + { + Results->CrossedWater = §ors[CurSector->sectornum]; + Results->CrossedWaterPos = { Results->X, Results->Y, Results->Z }; + } + Results = res; + } + return true; } +//========================================================================== +// +// +// +//========================================================================== + bool FTraceInfo::CheckPlane (const secplane_t &plane) { fixed_t den = TMulScale16 (plane.a, Vx, plane.b, Vy, plane.c, Vz); @@ -667,37 +918,11 @@ bool FTraceInfo::CheckPlane (const secplane_t &plane) return false; } -bool FTraceInfo::CheckSectorPlane (const sector_t *sector, bool checkFloor) -{ - secplane_t plane; - - if (checkFloor) - { - plane = sector->floorplane; - } - else - { - plane = sector->ceilingplane; - } - - return CheckPlane(plane); -} - -bool FTraceInfo::Check3DFloorPlane (const F3DFloor *ffloor, bool checkBottom) -{ - secplane_t plane; - - if (checkBottom) - { - plane = *(ffloor->bottom.plane); - } - else - { - plane = *(ffloor->top.plane); - } - - return CheckPlane(plane); -} +//========================================================================== +// +// +// +//========================================================================== static bool EditTraceResult (DWORD flags, FTraceResults &res) { diff --git a/src/p_trace.h b/src/p_trace.h index 1b0815833..16f440037 100644 --- a/src/p_trace.h +++ b/src/p_trace.h @@ -78,7 +78,9 @@ struct FTraceResults BYTE Tier; ETraceResult HitType; sector_t *CrossedWater; // For Boom-style, Transfer_Heights-based deep water + fixedvec3 CrossedWaterPos; // remember the position so that we can use it for spawning the splash F3DFloor *Crossed3DWater; // For 3D floor-based deep water + fixedvec3 Crossed3DWaterPos; F3DFloor *ffloor; };