2016-03-01 15:47:10 +00:00
|
|
|
/*
|
|
|
|
** p_trace.cpp
|
|
|
|
** Generalized trace function, like most 3D games have
|
|
|
|
**
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
** Copyright 1998-2006 Randy Heit
|
|
|
|
** All rights reserved.
|
|
|
|
**
|
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
|
|
|
**
|
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
|
|
** documentation and/or other materials provided with the distribution.
|
|
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
|
|
** derived from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
**---------------------------------------------------------------------------
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "p_trace.h"
|
|
|
|
#include "p_local.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "r_sky.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "p_maputl.h"
|
|
|
|
#include "r_defs.h"
|
|
|
|
#include "p_spec.h"
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2016-03-01 15:47:10 +00:00
|
|
|
struct FTraceInfo
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
DVector3 Start;
|
|
|
|
DVector3 Vec;
|
2016-03-01 15:47:10 +00:00
|
|
|
ActorFlags ActorMask;
|
|
|
|
DWORD WallMask;
|
|
|
|
AActor *IgnoreThis;
|
|
|
|
FTraceResults *Results;
|
2016-03-05 20:44:31 +00:00
|
|
|
FTraceResults *TempResults;
|
2016-03-01 15:47:10 +00:00
|
|
|
sector_t *CurSector;
|
2016-03-30 14:30:22 +00:00
|
|
|
double MaxDist;
|
|
|
|
double EnterDist;
|
2016-03-01 15:47:10 +00:00
|
|
|
ETraceStatus (*TraceCallback)(FTraceResults &res, void *data);
|
|
|
|
void *TraceCallbackData;
|
|
|
|
DWORD TraceFlags;
|
|
|
|
int inshootthrough;
|
2016-03-30 14:30:22 +00:00
|
|
|
double startfrac;
|
2016-03-05 20:44:31 +00:00
|
|
|
int aimdir;
|
2016-03-30 14:30:22 +00:00
|
|
|
double limitz;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
// These are required for 3D-floor checking
|
|
|
|
// to create a fake sector with a floor
|
|
|
|
// or ceiling plane coming from a 3D-floor
|
|
|
|
sector_t DummySector[2];
|
|
|
|
int sectorsel;
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
void Setup3DFloors();
|
|
|
|
bool LineCheck(intercept_t *in);
|
|
|
|
bool ThingCheck(intercept_t *in);
|
2016-03-01 15:47:10 +00:00
|
|
|
bool TraceTraverse (int ptflags);
|
|
|
|
bool CheckPlane(const secplane_t &plane);
|
2016-03-30 14:30:22 +00:00
|
|
|
int EnterLinePortal(line_t *li, double frac);
|
|
|
|
void EnterSectorPortal(int position, double frac, sector_t *entersec);
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
bool CheckSectorPlane(const sector_t *sector, bool checkFloor)
|
|
|
|
{
|
|
|
|
return CheckPlane(checkFloor ? sector->floorplane : sector->ceilingplane);
|
|
|
|
}
|
|
|
|
|
2016-03-06 20:58:36 +00:00
|
|
|
bool Check3DFloorPlane(const F3DFloor *ffloor, bool checkBottom)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
return CheckPlane(checkBottom? *(ffloor->bottom.plane) : *(ffloor->top.plane));
|
|
|
|
}
|
|
|
|
|
2016-03-06 20:58:36 +00:00
|
|
|
void SetSourcePosition()
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->SrcFromTarget = Start;
|
|
|
|
Results->HitVector = Vec;
|
2016-03-27 18:58:01 +00:00
|
|
|
Results->SrcAngleFromTarget = Results->HitVector.Angle();
|
2016-03-06 20:58:36 +00:00
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
|
2016-03-01 15:47:10 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static bool EditTraceResult (DWORD flags, FTraceResults &res);
|
|
|
|
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Trace entry point
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
bool Trace(const DVector3 &start, sector_t *sector, const DVector3 &direction, double maxDist,
|
|
|
|
ActorFlags actorMask, DWORD wallMask, AActor *ignore, FTraceResults &res, DWORD flags,
|
|
|
|
ETraceStatus(*callback)(FTraceResults &res, void *), void *callbackdata)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
|
|
|
int ptflags;
|
|
|
|
FTraceInfo inf;
|
2016-03-05 20:44:31 +00:00
|
|
|
FTraceResults tempResult;
|
|
|
|
|
|
|
|
memset(&tempResult, 0, sizeof(tempResult));
|
2016-03-23 17:07:04 +00:00
|
|
|
tempResult.Fraction = tempResult.Distance = NO_VALUE;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
ptflags = actorMask ? PT_ADDLINES|PT_ADDTHINGS|PT_COMPATIBLE : PT_ADDLINES;
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
inf.Start = start;
|
|
|
|
inf.Vec = direction;
|
2016-03-01 15:47:10 +00:00
|
|
|
inf.ActorMask = actorMask;
|
|
|
|
inf.WallMask = wallMask;
|
|
|
|
inf.IgnoreThis = ignore;
|
|
|
|
inf.CurSector = sector;
|
|
|
|
inf.MaxDist = maxDist;
|
|
|
|
inf.EnterDist = 0;
|
|
|
|
inf.TraceCallback = callback;
|
|
|
|
inf.TraceCallbackData = callbackdata;
|
|
|
|
inf.TraceFlags = flags;
|
|
|
|
inf.Results = &res;
|
2016-03-05 20:44:31 +00:00
|
|
|
inf.TempResults = &tempResult;
|
2016-03-01 15:47:10 +00:00
|
|
|
inf.inshootthrough = true;
|
|
|
|
inf.sectorsel=0;
|
2016-03-05 20:44:31 +00:00
|
|
|
inf.aimdir = -1;
|
|
|
|
inf.startfrac = 0;
|
2016-03-01 15:47:10 +00:00
|
|
|
memset(&res, 0, sizeof(res));
|
|
|
|
|
|
|
|
if (inf.TraceTraverse (ptflags))
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
2016-03-06 00:41:52 +00:00
|
|
|
return flags ? EditTraceResult(flags, res) : true;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// traverses a sector portal
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
void FTraceInfo::EnterSectorPortal(int position, double frac, sector_t *entersec)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
if (aimdir != -1 && aimdir != position) return;
|
|
|
|
AActor *portal = entersec->SkyBoxes[position];
|
|
|
|
|
2016-03-31 07:23:14 +00:00
|
|
|
if (aimdir == sector_t::ceiling && portal->specialf1 < limitz) return;
|
|
|
|
else if (aimdir == sector_t::floor && portal->specialf1 > limitz) return;
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
FTraceInfo newtrace;
|
|
|
|
FTraceResults results;
|
|
|
|
|
|
|
|
memset(&results, 0, sizeof(results));
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
newtrace.Start += portal->Scale;
|
2016-03-05 20:44:31 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
frac += 1 / MaxDist;
|
|
|
|
double enterdist = MaxDist * frac;
|
|
|
|
DVector2 enter = newtrace.Start.XY() + enterdist * Vec.XY();
|
2016-03-05 20:44:31 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
newtrace.Vec = Vec;
|
2016-03-05 20:44:31 +00:00
|
|
|
newtrace.ActorMask = ActorMask;
|
|
|
|
newtrace.WallMask = WallMask;
|
|
|
|
newtrace.IgnoreThis = IgnoreThis;
|
|
|
|
newtrace.Results = &results;
|
|
|
|
newtrace.TempResults = TempResults;
|
2016-03-30 14:30:22 +00:00
|
|
|
newtrace.CurSector = P_PointInSector(enter);
|
2016-03-05 20:44:31 +00:00
|
|
|
newtrace.MaxDist = MaxDist;
|
|
|
|
newtrace.EnterDist = EnterDist;
|
|
|
|
newtrace.TraceCallback = TraceCallback;
|
|
|
|
newtrace.TraceCallbackData = TraceCallbackData;
|
|
|
|
newtrace.TraceFlags = TraceFlags;
|
|
|
|
newtrace.inshootthrough = true;
|
|
|
|
newtrace.startfrac = frac;
|
|
|
|
newtrace.aimdir = position;
|
2016-03-30 14:30:22 +00:00
|
|
|
newtrace.limitz = portal->specialf1;
|
2016-03-05 20:44:31 +00:00
|
|
|
newtrace.sectorsel = 0;
|
|
|
|
|
|
|
|
if (newtrace.TraceTraverse(ActorMask ? PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE : PT_ADDLINES))
|
|
|
|
{
|
2016-03-06 20:58:36 +00:00
|
|
|
TempResults->CopyIfCloser(newtrace.Results);
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// traverses a line portal
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
int FTraceInfo::EnterLinePortal(line_t *li, double frac)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
FLinePortal *port = li->getPortal();
|
|
|
|
|
2016-03-06 00:04:19 +00:00
|
|
|
// The caller cannot handle portals without global offset.
|
|
|
|
if (port->mType != PORTT_LINKED && (TraceFlags & TRACE_PortalRestrict)) return -1;
|
|
|
|
|
|
|
|
FTraceInfo newtrace;
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
newtrace.Start = Start;
|
|
|
|
newtrace.Vec = Vec;
|
2016-03-06 00:04:19 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
P_TranslatePortalXY(li, newtrace.Start.X, newtrace.Start.Y);
|
|
|
|
P_TranslatePortalZ(li, newtrace.Start.Z);
|
|
|
|
P_TranslatePortalVXVY(li, newtrace.Vec.X, newtrace.Vec.Y);
|
2016-03-06 00:04:19 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
frac += 1 / MaxDist;
|
|
|
|
double enterdist = MaxDist / frac;
|
|
|
|
DVector2 enter = newtrace.Start.XY() + enterdist * Vec.XY();
|
2016-03-06 00:04:19 +00:00
|
|
|
|
|
|
|
newtrace.ActorMask = ActorMask;
|
|
|
|
newtrace.WallMask = WallMask;
|
|
|
|
newtrace.IgnoreThis = IgnoreThis;
|
|
|
|
newtrace.Results = Results;
|
|
|
|
newtrace.TempResults = TempResults;
|
2016-03-30 14:30:22 +00:00
|
|
|
newtrace.CurSector = P_PointInSector(enter);
|
2016-03-06 00:04:19 +00:00
|
|
|
newtrace.MaxDist = MaxDist;
|
|
|
|
newtrace.EnterDist = EnterDist;
|
|
|
|
newtrace.TraceCallback = TraceCallback;
|
|
|
|
newtrace.TraceCallbackData = TraceCallbackData;
|
|
|
|
newtrace.TraceFlags = TraceFlags;
|
|
|
|
newtrace.inshootthrough = true;
|
|
|
|
newtrace.startfrac = frac;
|
2016-03-05 20:44:31 +00:00
|
|
|
newtrace.aimdir = aimdir;
|
2016-03-06 00:04:19 +00:00
|
|
|
newtrace.limitz = limitz;
|
|
|
|
P_TranslatePortalZ(li, newtrace.limitz);
|
|
|
|
newtrace.sectorsel = 0;
|
|
|
|
Results->unlinked = true;
|
|
|
|
return newtrace.TraceTraverse(ActorMask ? PT_ADDLINES | PT_ADDTHINGS | PT_COMPATIBLE : PT_ADDLINES);
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Checks 3D floors at trace start and sets up related data
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
void FTraceInfo::Setup3DFloors()
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-04 00:12:38 +00:00
|
|
|
TDeletingArray<F3DFloor*> &ff = CurSector->e->XFloor.ffloors;
|
|
|
|
|
|
|
|
if (ff.Size())
|
|
|
|
{
|
|
|
|
memcpy(&DummySector[0], CurSector, sizeof(sector_t));
|
|
|
|
CurSector = &DummySector[0];
|
|
|
|
sectorsel = 1;
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
double sdist = MaxDist * startfrac;
|
|
|
|
DVector3 pos = Start + Vec * sdist;
|
2016-03-04 00:12:38 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
double bf = CurSector->floorplane.ZatPoint(pos);
|
|
|
|
double bc = CurSector->ceilingplane.ZatPoint(pos);
|
2016-03-04 00:12:38 +00:00
|
|
|
|
|
|
|
for (auto rover : ff)
|
|
|
|
{
|
|
|
|
if (!(rover->flags&FF_EXISTS))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL)
|
|
|
|
{
|
|
|
|
if (Check3DFloorPlane(rover, false))
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
2016-03-04 00:12:38 +00:00
|
|
|
Results->Crossed3DWater = rover;
|
2016-03-06 20:58:36 +00:00
|
|
|
Results->Crossed3DWaterPos = Results->HitPos;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-04 00:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(rover->flags&FF_SHOOTTHROUGH))
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
double ff_bottom = rover->bottom.plane->ZatPoint(pos);
|
|
|
|
double ff_top = rover->top.plane->ZatPoint(pos);
|
2016-03-04 00:12:38 +00:00
|
|
|
// clip to the part of the sector we are in
|
2016-03-30 14:30:22 +00:00
|
|
|
if (pos.Z > ff_top)
|
2016-03-04 00:12:38 +00:00
|
|
|
{
|
|
|
|
// above
|
2016-03-05 20:44:31 +00:00
|
|
|
if (bf < ff_top)
|
2016-03-04 00:12:38 +00:00
|
|
|
{
|
|
|
|
CurSector->floorplane = *rover->top.plane;
|
|
|
|
CurSector->SetTexture(sector_t::floor, *rover->top.texture, false);
|
2016-03-13 14:15:54 +00:00
|
|
|
CurSector->SkyBoxes[sector_t::floor] = nullptr;
|
2016-03-04 00:12:38 +00:00
|
|
|
bf = ff_top;
|
|
|
|
}
|
|
|
|
}
|
2016-03-30 14:30:22 +00:00
|
|
|
else if (pos.Z < ff_bottom)
|
2016-03-04 00:12:38 +00:00
|
|
|
{
|
|
|
|
//below
|
2016-03-05 20:44:31 +00:00
|
|
|
if (bc > ff_bottom)
|
2016-03-04 00:12:38 +00:00
|
|
|
{
|
|
|
|
CurSector->ceilingplane = *rover->bottom.plane;
|
|
|
|
CurSector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false);
|
|
|
|
bc = ff_bottom;
|
2016-03-13 14:15:54 +00:00
|
|
|
CurSector->SkyBoxes[sector_t::ceiling] = nullptr;
|
2016-03-04 00:12:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// inside
|
2016-03-05 20:44:31 +00:00
|
|
|
if (bf < ff_bottom)
|
2016-03-04 00:12:38 +00:00
|
|
|
{
|
|
|
|
CurSector->floorplane = *rover->bottom.plane;
|
|
|
|
CurSector->SetTexture(sector_t::floor, *rover->bottom.texture, false);
|
2016-03-13 14:15:54 +00:00
|
|
|
CurSector->SkyBoxes[sector_t::floor] = nullptr;
|
2016-03-04 00:12:38 +00:00
|
|
|
bf = ff_bottom;
|
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (bc > ff_top)
|
2016-03-04 00:12:38 +00:00
|
|
|
{
|
|
|
|
CurSector->ceilingplane = *rover->top.plane;
|
|
|
|
CurSector->SetTexture(sector_t::ceiling, *rover->top.texture, false);
|
2016-03-13 14:15:54 +00:00
|
|
|
CurSector->SkyBoxes[sector_t::ceiling] = nullptr;
|
2016-03-04 00:12:38 +00:00
|
|
|
bc = ff_top;
|
|
|
|
}
|
|
|
|
inshootthrough = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
if (!CurSector->PortalBlocksMovement(sector_t::ceiling))
|
|
|
|
{
|
|
|
|
EnterSectorPortal(sector_t::ceiling, startfrac, CurSector);
|
|
|
|
}
|
|
|
|
if (!CurSector->PortalBlocksMovement(sector_t::floor))
|
|
|
|
{
|
|
|
|
EnterSectorPortal(sector_t::floor, startfrac, CurSector);
|
|
|
|
}
|
|
|
|
}
|
2016-03-04 00:12:38 +00:00
|
|
|
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Processes one line intercept
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
bool FTraceInfo::LineCheck(intercept_t *in)
|
|
|
|
{
|
|
|
|
int lineside;
|
|
|
|
sector_t *entersector;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-31 14:52:25 +00:00
|
|
|
double dist = MaxDist * in->frac;
|
2016-03-30 14:30:22 +00:00
|
|
|
DVector3 hit = Start + Vec * dist;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
double ff, fc, bf = 0, bc = 0;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
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)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
lineside = 0;
|
|
|
|
CurSector = in->d.line->frontsector;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
lineside = P_PointOnLineSide(Start, in->d.line);
|
2016-03-05 20:44:31 +00:00
|
|
|
CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector;
|
|
|
|
}
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (!(in->d.line->flags & ML_TWOSIDED))
|
|
|
|
{
|
|
|
|
entersector = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
// 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)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_PCross);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
if (TraceFlags & TRACE_Impact)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
ff = CurSector->floorplane.ZatPoint(hit);
|
|
|
|
fc = CurSector->ceilingplane.ZatPoint(hit);
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (entersector != NULL)
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
bf = entersector->floorplane.ZatPoint(hit);
|
|
|
|
bc = entersector->ceilingplane.ZatPoint(hit);
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
sector_t *hsec = CurSector->GetHeightSec();
|
|
|
|
if (Results->CrossedWater == NULL &&
|
|
|
|
hsec != NULL &&
|
|
|
|
//CurSector->heightsec->waterzone &&
|
2016-03-30 14:30:22 +00:00
|
|
|
hit.Z <= hsec->floorplane.ZatPoint(hit))
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
// hit crossed a water plane
|
|
|
|
if (CheckSectorPlane(hsec, true))
|
|
|
|
{
|
|
|
|
Results->CrossedWater = §ors[CurSector->sectornum];
|
2016-03-06 20:58:36 +00:00
|
|
|
Results->CrossedWaterPos = Results->HitPos;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
if (hit.Z <= ff)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-03-30 14:30:22 +00:00
|
|
|
else if (hit.Z >= fc)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-03-06 00:04:19 +00:00
|
|
|
else if (in->d.line->isLinePortal())
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
if (entersector == NULL || (hit.Z >= bf && hit.Z <= bc))
|
2016-03-06 00:04:19 +00:00
|
|
|
{
|
2016-03-31 14:52:25 +00:00
|
|
|
int res = EnterLinePortal(in->d.line, in->frac);
|
2016-03-06 00:04:19 +00:00
|
|
|
if (res != -1)
|
|
|
|
{
|
|
|
|
aimdir = INT_MAX; // flag for ending the traverse
|
|
|
|
return !!res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto normalline; // hit upper or lower tier.
|
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
else if (entersector == NULL ||
|
2016-03-30 14:30:22 +00:00
|
|
|
hit.Z < bf || hit.Z > bc ||
|
2016-03-05 20:44:31 +00:00
|
|
|
in->d.line->flags & WallMask)
|
|
|
|
{
|
2016-03-06 00:04:19 +00:00
|
|
|
normalline:
|
2016-03-05 20:44:31 +00:00
|
|
|
// hit the wall
|
|
|
|
Results->HitType = TRACE_HitWall;
|
|
|
|
Results->Tier =
|
|
|
|
entersector == NULL ? TIER_Middle :
|
2016-03-30 14:30:22 +00:00
|
|
|
hit.Z <= bf ? TIER_Lower :
|
|
|
|
hit.Z >= bc ? TIER_Upper : TIER_Middle;
|
2016-03-05 20:44:31 +00:00
|
|
|
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;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
for (auto rover : entersector->e->XFloor.ffloors)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH);
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
double ff_bottom = rover->bottom.plane->ZatPoint(hit);
|
|
|
|
double ff_top = rover->top.plane->ZatPoint(hit);
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
// clip to the part of the sector we are in
|
2016-03-30 14:30:22 +00:00
|
|
|
if (hit.Z > ff_top)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
// above
|
|
|
|
if (bf < ff_top)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
entersector->floorplane = *rover->top.plane;
|
|
|
|
entersector->SetTexture(sector_t::floor, *rover->top.texture, false);
|
|
|
|
entersector->SkyBoxes[sector_t::floor] = NULL;
|
|
|
|
bf = ff_top;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-30 14:30:22 +00:00
|
|
|
else if (hit.Z < ff_bottom)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
//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;
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
//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)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
goto cont;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
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 (!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)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
Results->HitType = TRACE_HitWall;
|
|
|
|
Results->Tier = TIER_Middle;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
else
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
if (hit.Z <= bf || hit.Z >= bc)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
Results->HitType = TRACE_HitWall;
|
|
|
|
Results->Tier =
|
2016-03-30 14:30:22 +00:00
|
|
|
hit.Z <= bf ? TIER_Lower :
|
|
|
|
hit.Z >= bc ? TIER_Upper : TIER_Middle;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
else
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
Results->HitType = TRACE_HitNone;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_Impact);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (Results->HitType == TRACE_HitWall)
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->HitPos = hit;
|
2016-03-06 20:58:36 +00:00
|
|
|
SetSourcePosition();
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->Distance = dist;
|
2016-03-31 14:52:25 +00:00
|
|
|
Results->Fraction = in->frac;
|
2016-03-05 20:44:31 +00:00
|
|
|
Results->Line = in->d.line;
|
|
|
|
Results->Side = lineside;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TraceCallback != NULL && Results->HitType != TRACE_HitNone)
|
|
|
|
{
|
|
|
|
switch (TraceCallback(*Results, TraceCallbackData))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
case TRACE_Stop: return false;
|
|
|
|
case TRACE_Abort: Results->HitType = TRACE_HitNone; return false;
|
|
|
|
case TRACE_Skip: Results->HitType = TRACE_HitNone; break;
|
|
|
|
default: break;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (Results->HitType == TRACE_HitNone)
|
|
|
|
{
|
|
|
|
CurSector = entersector;
|
|
|
|
EnterDist = dist;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
bool FTraceInfo::ThingCheck(intercept_t *in)
|
|
|
|
{
|
2016-03-31 14:52:25 +00:00
|
|
|
double dist = MaxDist * in->frac;
|
2016-03-30 14:30:22 +00:00
|
|
|
DVector3 hit = Start + Vec * dist;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
if (hit.Z > in->d.thing->Top())
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
// trace enters above actor
|
2016-03-30 14:30:22 +00:00
|
|
|
if (Vec.Z >= 0) return true; // Going up: can't hit
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
// Does it hit the top of the actor?
|
2016-03-30 14:30:22 +00:00
|
|
|
dist = (in->d.thing->Top() - Start.Z) / Vec.Z;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (dist > MaxDist) return true;
|
2016-03-31 14:52:25 +00:00
|
|
|
in->frac = dist / MaxDist;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
hit = Start + Vec * dist;
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
// calculated coordinate is outside the actor's bounding box
|
2016-03-30 14:30:22 +00:00
|
|
|
if (fabs(hit.X - in->d.thing->X()) > in->d.thing->radius ||
|
|
|
|
fabs(hit.Y - in->d.thing->Y()) > in->d.thing->radius) return true;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-30 14:30:22 +00:00
|
|
|
else if (hit.Z < in->d.thing->Z())
|
2016-03-05 20:44:31 +00:00
|
|
|
{ // trace enters below actor
|
2016-03-30 14:30:22 +00:00
|
|
|
if (Vec.Z <= 0) return true; // Going down: can't hit
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
// Does it hit the bottom of the actor?
|
2016-03-30 14:30:22 +00:00
|
|
|
dist = (in->d.thing->Z() - Start.Z) / Vec.Z;
|
2016-03-05 20:44:31 +00:00
|
|
|
if (dist > MaxDist) return true;
|
2016-03-31 14:52:25 +00:00
|
|
|
in->frac = dist / MaxDist;
|
2016-03-05 20:44:31 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
hit = Start + Vec * dist;
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
// calculated coordinate is outside the actor's bounding box
|
2016-03-30 14:30:22 +00:00
|
|
|
if (fabs(hit.X - in->d.thing->X()) > in->d.thing->radius ||
|
|
|
|
fabs(hit.Y - in->d.thing->Y()) > in->d.thing->radius) return true;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
if (CurSector->e->XFloor.ffloors.Size())
|
|
|
|
{
|
|
|
|
// check for 3D floor hits first.
|
2016-03-30 14:30:22 +00:00
|
|
|
double ff_floor = CurSector->floorplane.ZatPoint(hit);
|
|
|
|
double ff_ceiling = CurSector->ceilingplane.ZatPoint(hit);
|
2016-03-05 20:44:31 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
if (hit.Z > ff_ceiling && CurSector->PortalBlocksMovement(sector_t::ceiling)) // actor is hit above the current ceiling
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
Results->HitType = TRACE_HitCeiling;
|
|
|
|
Results->HitTexture = CurSector->GetTexture(sector_t::ceiling);
|
|
|
|
}
|
2016-03-30 14:30:22 +00:00
|
|
|
else if (hit.Z < ff_floor && CurSector->PortalBlocksMovement(sector_t::floor)) // actor is hit below the current floor
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
Results->HitType = TRACE_HitFloor;
|
|
|
|
Results->HitTexture = CurSector->GetTexture(sector_t::floor);
|
|
|
|
}
|
|
|
|
else goto cont1;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
// 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))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
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;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cont1:
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
Results->HitType = TRACE_HitActor;
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->HitPos = hit;
|
2016-03-06 20:58:36 +00:00
|
|
|
SetSourcePosition();
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->Distance = dist;
|
2016-03-31 14:52:25 +00:00
|
|
|
Results->Fraction = in->frac;
|
2016-03-05 20:44:31 +00:00
|
|
|
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();
|
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
FPathTraverse it(Start.X, Start.Y, Vec.X * MaxDist, Vec.Y * MaxDist, ptflags | PT_DELTA, startfrac);
|
2016-03-05 20:44:31 +00:00
|
|
|
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)
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
if ((rover->flags & FF_EXISTS) && (rover->flags&FF_SWIMMABLE))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
if (Check3DFloorPlane(rover, false))
|
|
|
|
{
|
|
|
|
Results->Crossed3DWater = rover;
|
2016-03-06 20:58:36 +00:00
|
|
|
Results->Crossed3DWaterPos = Results->HitPos;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
lastsplashsector = CurSector->sectornum;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
// We have something closer in the storage for portal subtraces.
|
2016-03-31 14:52:25 +00:00
|
|
|
if (TempResults->HitType != TRACE_HitNone && in->frac > TempResults->Fraction)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (in->isaline)
|
|
|
|
{
|
2016-03-08 10:55:40 +00:00
|
|
|
bool res = LineCheck(in);
|
|
|
|
if (aimdir == INT_MAX) return res; // signal for immediate abort
|
|
|
|
if (!res) break;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
else if ((in->d.thing->flags & ActorMask) && in->d.thing != IgnoreThis)
|
|
|
|
{
|
|
|
|
if (!ThingCheck(in)) break;
|
|
|
|
}
|
|
|
|
}
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
// Check if one subtrace through a portal yielded a better result.
|
|
|
|
if (TempResults->HitType != TRACE_HitNone &&
|
|
|
|
(Results->HitType == TRACE_HitNone || Results->Fraction > TempResults->Fraction))
|
|
|
|
{
|
2016-03-06 01:07:04 +00:00
|
|
|
// We still need to do a water check here or this may get missed on occasion
|
|
|
|
if (Results->CrossedWater == NULL &&
|
|
|
|
CurSector->heightsec != NULL &&
|
2016-03-23 17:07:04 +00:00
|
|
|
CurSector->heightsec->floorplane.ZatPoint(Results->HitPos) >= Results->HitPos.Z)
|
2016-03-06 01:07:04 +00:00
|
|
|
{
|
|
|
|
// 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];
|
2016-03-06 20:58:36 +00:00
|
|
|
Results->CrossedWaterPos = Results->HitPos;
|
2016-03-06 01:07:04 +00:00
|
|
|
}
|
|
|
|
Results = res;
|
|
|
|
}
|
|
|
|
|
|
|
|
Results->CopyIfCloser(TempResults);
|
2016-03-05 20:44:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (Results->HitType == TRACE_HitNone)
|
|
|
|
{
|
|
|
|
if (CurSector->PortalBlocksMovement(sector_t::floor) && CheckSectorPlane(CurSector, true))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
Results->HitType = TRACE_HitFloor;
|
|
|
|
Results->HitTexture = CurSector->GetTexture(sector_t::floor);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
else if (CurSector->PortalBlocksMovement(sector_t::ceiling) && CheckSectorPlane(CurSector, false))
|
2016-03-01 15:47:10 +00:00
|
|
|
{
|
2016-03-05 20:44:31 +00:00
|
|
|
Results->HitType = TRACE_HitCeiling;
|
|
|
|
Results->HitTexture = CurSector->GetTexture(sector_t::ceiling);
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-03-05 20:44:31 +00:00
|
|
|
|
|
|
|
// check for intersection with floor/ceiling
|
|
|
|
Results->Sector = §ors[CurSector->sectornum];
|
|
|
|
|
|
|
|
if (Results->CrossedWater == NULL &&
|
|
|
|
CurSector->heightsec != NULL &&
|
2016-03-23 17:07:04 +00:00
|
|
|
CurSector->heightsec->floorplane.ZatPoint(Results->HitPos) >= Results->HitPos.Z)
|
2016-03-05 20:44:31 +00:00
|
|
|
{
|
|
|
|
// 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];
|
2016-03-06 20:58:36 +00:00
|
|
|
Results->CrossedWaterPos = Results->HitPos;
|
2016-03-05 20:44:31 +00:00
|
|
|
}
|
|
|
|
Results = res;
|
|
|
|
}
|
2016-03-06 00:41:52 +00:00
|
|
|
if (Results->HitType == TRACE_HitNone && Results->Distance == 0)
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->HitPos = Start + Vec * MaxDist;
|
2016-03-06 20:58:36 +00:00
|
|
|
SetSourcePosition();
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->Distance = MaxDist;
|
2016-03-23 17:07:04 +00:00
|
|
|
Results->Fraction = 1.;
|
2016-03-06 00:41:52 +00:00
|
|
|
}
|
|
|
|
return Results->HitType != TRACE_HitNone;
|
2016-03-01 15:47:10 +00:00
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2016-03-01 15:47:10 +00:00
|
|
|
bool FTraceInfo::CheckPlane (const secplane_t &plane)
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
double den = plane.fA() * Vec.X + plane.fB() * Vec.Y + plane.fC() * Vec.Z;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
if (den != 0)
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
double num = plane.fA() * Start.X + plane.fB() * Start.Y + plane.fC() * Start.Z + plane.fD();
|
2016-03-01 15:47:10 +00:00
|
|
|
|
2016-03-30 14:30:22 +00:00
|
|
|
double hitdist = -num / den;
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
if (hitdist > EnterDist && hitdist < MaxDist)
|
|
|
|
{
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->HitPos = Start + Vec * hitdist;
|
2016-03-06 20:58:36 +00:00
|
|
|
SetSourcePosition();
|
2016-03-30 14:30:22 +00:00
|
|
|
Results->Distance = hitdist;
|
|
|
|
Results->Fraction = hitdist / MaxDist;
|
2016-03-01 15:47:10 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-05 20:44:31 +00:00
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2016-03-01 15:47:10 +00:00
|
|
|
|
|
|
|
static bool EditTraceResult (DWORD flags, FTraceResults &res)
|
|
|
|
{
|
|
|
|
if (flags & TRACE_NoSky)
|
|
|
|
{ // Throw away sky hits
|
|
|
|
if (res.HitType == TRACE_HitFloor || res.HitType == TRACE_HitCeiling)
|
|
|
|
{
|
|
|
|
if (res.HitTexture == skyflatnum)
|
|
|
|
{
|
|
|
|
res.HitType = TRACE_HitNone;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (res.HitType == TRACE_HitWall)
|
|
|
|
{
|
|
|
|
if (res.Tier == TIER_Upper &&
|
|
|
|
res.Line->frontsector->GetTexture(sector_t::ceiling) == skyflatnum &&
|
|
|
|
res.Line->backsector->GetTexture(sector_t::ceiling) == skyflatnum)
|
|
|
|
{
|
|
|
|
res.HitType = TRACE_HitNone;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|