Classes:LineTracer
Note: This feature is for ZScript only. |
LineTracer is a class that allows for custom ray tracing (aka hitscan) behavior. It uses the same tracing technique that LineTrace and LineAttack use, but with some notable differences:
- LineTracer acts similar to a projectile rather than a hitscan. This means that rather than obtaining a specific point, it actually passes through the map, reading its blockmap. This allows for custom collision logic and passing through multiple actors and geometry.
- LineTracer is data-scoped, in contrast to LineTrace, which is play-scoped. This allows it to be instantiated from any scope, for example, firing it from a HUD to obtain some blockmap data(of course, in this case it won't be able to perform play-scoped changes, like dealing damage).
If there's no need to pass through geometry or actors, LineTrace should be used instead because it's easier to set up.
Note: LineTracer is not able to detect actors that aren't a part of the blockmap due to having the NOBLOCKMAP flag. |
Setting up
Setting up a LineTracer requires several steps:
- First, a custom class based on LineTracer must be created. You can add custom fields to that class, as usual. Note, LineTracer is not an Actor, so it can't use Actor flags or the Default block.
- Override TraceCallBack() in this new class to add custom collision rules and manipulate data as necessary.
- To fire the LineTracer, use New() to instantiate it and Trace() to fire it.
Fields
The following fields are defined in the LineTracer class and can be read both inside its TraceCallBack call, and if you have a pointer to a LineTracer instance:
- @TraceResults Results
- A pointer to the TraceResults struct that contains information pertaining to what caused the trace to call TraceCallback.
TraceResults
A pointer to this struct is contained in the LineTracer's results field, as described above. This struct is normally read in a TraceCallBack override. Its fields can not only be read, but also modified to allow custom results. The following fields are available:
- ETraceResult HitType
- What the trace hit, if anything. Can be one of the following:
- TRACE_HitNone
- TRACE_HitFloor
- TRACE_HitCeiling
- TRACE_HitWall
- TRACE_HitActor
- TRACE_CrossingPortal (requires portal reporting flag in Trace to be set)
- TRACE_HasHitSky (requires hitting sky flag in Trace to be set)
- Sector HitSector
- The sector where the trace called TraceCallback()
- TextureID HitTexture
- The texture of the surface that got hit, if any
- Vector3 HitPos
- The absolute map coordinates where the trace called TraceCallback()
- Vector3 HitVector
- The direction the trace was traveling
- F3DFloor ffloor
- The 3D floor that got hit, if any.
- Vector3 SrcFromTarget
- The starting position of the trace
- double SrcAngleFromTarget
- The angle of
HitVector
- double Distance
- The distance from the starting point the trace has traveled
- double Fraction
- The fraction of the total distance the trace has traveled
- Actor HitActor
- The Actor that was hit, if any
- Line HitLine
- The Line that was hit, if any
- uint Side
- Which side of the line that was hit, if any. Can be one of the following:
- Line.front
- Line.back
- uint Tier
- Which part of the line was hit, if any. Can be one of the following:
- TIER_Middle
- TIER_Upper
- TIER_Lower
- TIER_FFloor (hit a line connected to a 3D floor)
- bool unlinked
True
if the trace passed through a portal that was not linked
- Sector CrossedWater
- The sector the trace hit that contained Boom-style deep water, if any
- Vector3 CrossedWaterPos
- The absolute map coordinates where the trace hit CrossedWater
- Sector Crossed3DWater
- The sector the trace hit that contained 3D floor-style deep water, if any
- Vector3 Crossed3DWaterPos
- The absolute map coordinates where the trace hit Crossed3DWater
Methods
Non-static
- bool Trace(vector3 start, Sector sec, vector3 direction, double maxDist, ETraceFlags traceFlags, uint wallMask = 0xFFFFFFFF, bool ignoreAllActors = false, Actor ignore = null)
This is the primary method for spawning and "firing" a LineTracer. Once you've instantiated a LineTracer with New, this function can be called on a pointer to the LineTracer to launch it. The function has the following parameters:
- vector3 start - The starting location of the trace. This is an absolute map coordinate
- Sector sec - The sector that the trace is starting from. If LineTracer is meant to be "fired" from an actor, pass that actor's
cursector
field here. - vector3 direction - A 3D vector that points in the direction the trace should start traveling
- double maxDist - How far the trace should travel before it terminates. Use
PLAYERMISSILERANGE
for player's standard hitscan distance (4096 units). - ETraceFlags traceFlags - Modifies the behavior of the trace. The following flags are available:
- TRACE_NoSky - Hitting the sky sets the
HitType
toTRACE_HitNone
- TRACE_PortalRestrict - Cannot travel through portals that are not linked
- TRACE_ReportPortals - Update Results to accurately reflect traveling through a portal. TraceCallback is called with a
HitType
ofTRACE_CrossingPortal
when traveling through one - TRACE_HitSky - Hitting the sky sets the
HitType
toTRACE_HasHitSky
- TRACE_NoSky - Hitting the sky sets the
Extra arguments were added in GZDoom 4.12:
- uint wallMask — This field accepts Line flags. If the default value (0xFFFFFFFF) is used,
results.HitType
will be set toTRACE_HitWall
each time LineTracer crosses *any* line (including non-blocking sector boundaries). Otherwise, only lines with the provided flags will set it toTRACE_HitWall
. Pass0
if you only want to detect one-sided lines and exclude everything else. - bool ignoreAllActors — If
true
, all actors will setHitType
toTRACE_HitNone
. - Actor ignore — A pointer to an actor that will be ignored by the LineTracer.
Return value: returns true
if LineTracer's results.HitType
is not TRACE_HitNone
. Returns false
otherwise.
Note: this function only returns a bool, which is not the same as the return value of TraceCallBack. The latter's return value must be read with <pointer to LineTracer>.results.HitType
.
Virtual
- ETraceStatus TraceCallback()
This is called during a trace when any Actor in the blockmap is hit, any line is hit, or any plane is hit. This can also be called when crossing portals if set to do so. Custom logic handling for what happens when something is hit happens in this function. Generally Results' HitType is looked at to determine what should be examined in the results.
Return value: returns the status of the trace. The function can be overridden to set the return value manually after passing custom conditions. The status is used to determine whether to keep going or terminate the trace at that spot. Can be one of the following values:
- TRACE_Stop - Terminates the trace at its current spot, returning the results from the current hit
- TRACE_Continue - Tells the trace to keep going. If nothing else is hit along the way, the results from the current hit will be returned
- TRACE_Skip - Similar to TRACE_Continue but will not return the results from the current hit
- TRACE_Abort - Similar to TRACE_Stop but will not return the results from the current hit
Note: if not overridden, this function returns TRACE_Stop
by default, causing it to stop at the first collision.
Examples
// This is meant to be a custom Weapon function that instantiates a custom LineTracer:
action void A_FindGhosts()
{
// Create the tracer
let tracer = new('GhostFinder');
if (!tracer)
{
return;
}
// Get the current direction the player is looking
Vector3 dir = (AngleToVector(angle, cos(pitch)), -sin(pitch));
// Set the starting point to the player's eye level:
Vector3 start = (pos.xy, player.viewz);
tracer.Trace(start, CurSector, dir, PLAYERMISSILERANGE, 0);
if (tracer.bHitGhost)
{
console.printf("Ahhh! A spooky ghost!");
}
}
// This is our custom LineTracer:
class GhostFinder : LineTracer
{
bool bHitGhost; //custom flag
// This is where we'll examine the results to check for ghosts
override ETraceStatus TraceCallback()
{
// Check if we hit an actor. If so, does it have the ghost flag?
if (results.HitType == TRACE_HitActor && results.HitActor.bGhost)
{
bHitGhost = true;
// Tell the trace to terminate
return TRACE_Stop;
}
// If we didn't hit an actor or it doesn't have the ghost flag, ignore it and keep going
return TRACE_Skip;
}
}