1 /*
2 ** p_trace.cpp
3 ** Generalized trace function, like most 3D games have
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include "p_trace.h"
36 #include "p_local.h"
37 #include "i_system.h"
38 #include "r_sky.h"
39 #include "doomstat.h"
40 
41 struct FTraceInfo
42 {
43 	fixed_t StartX, StartY, StartZ;
44 	fixed_t Vx, Vy, Vz;
45 	ActorFlags ActorMask;
46 	DWORD WallMask;
47 	AActor *IgnoreThis;
48 	FTraceResults *Results;
49 	sector_t *CurSector;
50 	fixed_t MaxDist;
51 	fixed_t EnterDist;
52 	ETraceStatus (*TraceCallback)(FTraceResults &res, void *data);
53 	void *TraceCallbackData;
54 	DWORD TraceFlags;
55 	int inshootthrough;
56 
57 	// These are required for 3D-floor checking
58 	// to create a fake sector with a floor
59 	// or ceiling plane coming from a 3D-floor
60 	sector_t DummySector[2];
61 	int sectorsel;
62 
63 	bool TraceTraverse (int ptflags);
64 	bool CheckPlane(const secplane_t &plane);
65 	bool CheckSectorPlane (const sector_t *sector, bool checkFloor);
66 	bool Check3DFloorPlane(const F3DFloor *ffloor, bool checkBottom);
67 };
68 
69 static bool EditTraceResult (DWORD flags, FTraceResults &res);
70 
71 
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,FTraceResults & res,DWORD flags,ETraceStatus (* callback)(FTraceResults & res,void *),void * callbackdata)72 bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector,
73 			fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist,
74 			ActorFlags actorMask, DWORD wallMask, AActor *ignore,
75 			FTraceResults &res,
76 			DWORD flags, ETraceStatus (*callback)(FTraceResults &res, void *), void *callbackdata)
77 {
78 	int ptflags;
79 	FTraceInfo inf;
80 
81 	ptflags = actorMask ? PT_ADDLINES|PT_ADDTHINGS|PT_COMPATIBLE : PT_ADDLINES;
82 
83 	inf.StartX = x;
84 	inf.StartY = y;
85 	inf.StartZ = z;
86 	inf.Vx = vx;
87 	inf.Vy = vy;
88 	inf.Vz = vz;
89 	inf.ActorMask = actorMask;
90 	inf.WallMask = wallMask;
91 	inf.IgnoreThis = ignore;
92 	inf.CurSector = sector;
93 	inf.MaxDist = maxDist;
94 	inf.EnterDist = 0;
95 	inf.TraceCallback = callback;
96 	inf.TraceCallbackData = callbackdata;
97 	inf.TraceFlags = flags;
98 	inf.Results = &res;
99 	inf.inshootthrough = true;
100 	inf.sectorsel=0;
101 	memset(&res, 0, sizeof(res));
102 	/* // Redundant with the memset
103 	res.HitType = TRACE_HitNone;
104 	res.CrossedWater = NULL;
105 	res.Crossed3DWater = NULL;
106 	*/
107 
108 	// Do a 3D floor check in the starting sector
109 	TDeletingArray<F3DFloor*> &ff = sector->e->XFloor.ffloors;
110 
111 	if (ff.Size())
112 	{
113 		memcpy(&inf.DummySector[0],sector,sizeof(sector_t));
114 		inf.CurSector=sector=&inf.DummySector[0];
115 		inf.sectorsel=1;
116 		fixed_t bf = sector->floorplane.ZatPoint (x, y);
117 		fixed_t bc = sector->ceilingplane.ZatPoint (x, y);
118 
119 		for(unsigned int i=0;i<ff.Size();i++)
120 		{
121 			F3DFloor * rover=ff[i];
122 			if (!(rover->flags&FF_EXISTS))
123 				continue;
124 
125 			if (rover->flags&FF_SWIMMABLE && res.Crossed3DWater == NULL)
126 			{
127 				if (inf.Check3DFloorPlane(rover, false))
128 					res.Crossed3DWater = rover;
129 			}
130 
131 			if (!(rover->flags&FF_SHOOTTHROUGH))
132 			{
133 				fixed_t ff_bottom=rover->bottom.plane->ZatPoint(x, y);
134 				fixed_t ff_top=rover->top.plane->ZatPoint(x, y);
135 				// clip to the part of the sector we are in
136 				if (z>ff_top)
137 				{
138 					// above
139 					if (bf<ff_top)
140 					{
141 						sector->floorplane=*rover->top.plane;
142 						sector->SetTexture(sector_t::floor, *rover->top.texture, false);
143 						bf=ff_top;
144 					}
145 				}
146 				else if (z<ff_bottom)
147 				{
148 					//below
149 					if (bc>ff_bottom)
150 					{
151 						sector->ceilingplane=*rover->bottom.plane;
152 						sector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false);
153 						bc=ff_bottom;
154 					}
155 				}
156 				else
157 				{
158 					// inside
159 					if (bf<ff_bottom)
160 					{
161 						sector->floorplane=*rover->bottom.plane;
162 						sector->SetTexture(sector_t::floor, *rover->bottom.texture, false);
163 						bf=ff_bottom;
164 					}
165 
166 					if (bc>ff_top)
167 					{
168 						sector->ceilingplane=*rover->top.plane;
169 						sector->SetTexture(sector_t::ceiling, *rover->top.texture, false);
170 						bc=ff_top;
171 					}
172 					inf.inshootthrough = false;
173 				}
174 			}
175 		}
176 	}
177 
178 	// check for overflows and clip if necessary
179 	SQWORD xd = (SQWORD)x + ( ( SQWORD(vx) * SQWORD(maxDist) )>>16);
180 
181 	if (xd>SQWORD(32767)*FRACUNIT)
182 	{
183 		maxDist = inf.MaxDist = FixedDiv(FIXED_MAX - x, vx);
184 	}
185 	else if (xd<-SQWORD(32767)*FRACUNIT)
186 	{
187 		maxDist = inf.MaxDist = FixedDiv(FIXED_MIN - x, vx);
188 	}
189 
190 
191 	SQWORD yd = (SQWORD)y + ( ( SQWORD(vy) * SQWORD(maxDist) )>>16);
192 
193 	if (yd>SQWORD(32767)*FRACUNIT)
194 	{
195 		maxDist = inf.MaxDist=FixedDiv(FIXED_MAX-y,vy);
196 	}
197 	else if (yd<-SQWORD(32767)*FRACUNIT)
198 	{
199 		maxDist = inf.MaxDist=FixedDiv(FIXED_MIN-y,vy);
200 	}
201 
202 	// recalculate the trace's end points for robustness
203 	if (inf.TraceTraverse (ptflags))
204 	{ // check for intersection with floor/ceiling
205 		res.Sector = &sectors[inf.CurSector->sectornum];
206 
207 		if (inf.CheckSectorPlane (inf.CurSector, true))
208 		{
209 			res.HitType = TRACE_HitFloor;
210 			res.HitTexture = inf.CurSector->GetTexture(sector_t::floor);
211 			if (res.CrossedWater == NULL &&
212 				inf.CurSector->heightsec != NULL &&
213 				inf.CurSector->heightsec->floorplane.ZatPoint (res.X, res.Y) >= res.Z)
214 			{
215 				res.CrossedWater = &sectors[inf.CurSector->sectornum];
216 			}
217 		}
218 		else if (inf.CheckSectorPlane (inf.CurSector, false))
219 		{
220 			res.HitType = TRACE_HitCeiling;
221 			res.HitTexture = inf.CurSector->GetTexture(sector_t::ceiling);
222 		}
223 	}
224 
225 	if (res.HitType != TRACE_HitNone)
226 	{
227 		if (flags)
228 		{
229 			return EditTraceResult (flags, res);
230 		}
231 		else
232 		{
233 			return true;
234 		}
235 	}
236 	else
237 	{
238 		res.HitType = TRACE_HitNone;
239 		res.X = x + FixedMul (vx, maxDist);
240 		res.Y = y + FixedMul (vy, maxDist);
241 		res.Z = z + FixedMul (vz, maxDist);
242 		res.Distance = maxDist;
243 		res.Fraction = FRACUNIT;
244 		return false;
245 	}
246 }
247 
TraceTraverse(int ptflags)248 bool FTraceInfo::TraceTraverse (int ptflags)
249 {
250 	FPathTraverse it(StartX, StartY, FixedMul (Vx, MaxDist), FixedMul (Vy, MaxDist), ptflags | PT_DELTA);
251 	intercept_t *in;
252 
253 	while ((in = it.Next()))
254 	{
255 		fixed_t hitx, hity, hitz;
256 		fixed_t dist;
257 
258 		// Deal with splashes in 3D floors
259 		if (CurSector->e->XFloor.ffloors.Size())
260 		{
261 			for(unsigned int i=0;i<CurSector->e->XFloor.ffloors.Size();i++)
262 			{
263 				F3DFloor * rover=CurSector->e->XFloor.ffloors[i];
264 				if (!(rover->flags&FF_EXISTS))
265 					continue;
266 
267 				// Deal with splashy stuff
268 				if (rover->flags&FF_SWIMMABLE && Results->Crossed3DWater == NULL)
269 				{
270 					if (Check3DFloorPlane(rover, false))
271 						Results->Crossed3DWater = rover;
272 				}
273 			}
274 		}
275 
276 		if (in->isaline)
277 		{
278 			int lineside;
279 			sector_t *entersector;
280 
281 			dist = FixedMul (MaxDist, in->frac);
282 			hitx = StartX + FixedMul (Vx, dist);
283 			hity = StartY + FixedMul (Vy, dist);
284 			hitz = StartZ + FixedMul (Vz, dist);
285 
286 			fixed_t ff, fc, bf = 0, bc = 0;
287 
288 			// CurSector may be a copy so we must compare the sector number, not the index!
289 			if (in->d.line->frontsector->sectornum == CurSector->sectornum)
290 			{
291 				lineside = 0;
292 			}
293 			else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum)
294 			{
295 				lineside = 1;
296 			}
297 			else
298 			{ // Dammit. Why does Doom have to allow non-closed sectors?
299 				if (in->d.line->backsector == NULL)
300 				{
301 					lineside = 0;
302 					CurSector = in->d.line->frontsector;
303 				}
304 				else
305 				{
306 					lineside = P_PointOnLineSide (StartX, StartY, in->d.line);
307 					CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector;
308 				}
309 			}
310 
311 			if (!(in->d.line->flags & ML_TWOSIDED))
312 			{
313 				entersector = NULL;
314 			}
315 			else
316 			{
317 				entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector;
318 
319 				// For backwards compatibility: Ignore lines with the same sector on both sides.
320 				// This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15) need it.
321 				if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector)
322 				{
323 					// We must check special activation here because the code below is never reached.
324 					if (TraceFlags & TRACE_PCross)
325 					{
326 						P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross);
327 					}
328 					if (TraceFlags & TRACE_Impact)
329 					{
330 						P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
331 					}
332 					continue;
333 				}
334 			}
335 
336 			ff = CurSector->floorplane.ZatPoint (hitx, hity);
337 			fc = CurSector->ceilingplane.ZatPoint (hitx, hity);
338 
339 			if (entersector != NULL)
340 			{
341 				bf = entersector->floorplane.ZatPoint (hitx, hity);
342 				bc = entersector->ceilingplane.ZatPoint (hitx, hity);
343 			}
344 
345 			sector_t *hsec = CurSector->GetHeightSec();
346 			if (Results->CrossedWater == NULL &&
347 				hsec != NULL &&
348 				//CurSector->heightsec->waterzone &&
349 				hitz <= hsec->floorplane.ZatPoint (hitx, hity))
350 			{
351 				// hit crossed a water plane
352 				Results->CrossedWater = &sectors[CurSector->sectornum];
353 			}
354 
355 			if (hitz <= ff)
356 			{ // hit floor in front of wall
357 				Results->HitType = TRACE_HitFloor;
358 				Results->HitTexture = CurSector->GetTexture(sector_t::floor);
359 			}
360 			else if (hitz >= fc)
361 			{ // hit ceiling in front of wall
362 				Results->HitType = TRACE_HitCeiling;
363 				Results->HitTexture = CurSector->GetTexture(sector_t::ceiling);
364 			}
365 			else if (entersector == NULL ||
366 				hitz < bf || hitz > bc ||
367 				in->d.line->flags & WallMask)
368 			{ // hit the wall
369 				Results->HitType = TRACE_HitWall;
370 				Results->Tier =
371 					entersector == NULL ? TIER_Middle :
372 					hitz <= bf ? TIER_Lower :
373 					hitz >= bc ? TIER_Upper : TIER_Middle;
374 				if (TraceFlags & TRACE_Impact)
375 				{
376 					P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
377 				}
378 			}
379 			else
380 			{ 	// made it past the wall
381 				// check for 3D floors first
382 				if (entersector->e->XFloor.ffloors.Size())
383 				{
384 					memcpy(&DummySector[sectorsel],entersector,sizeof(sector_t));
385 					entersector=&DummySector[sectorsel];
386 					sectorsel^=1;
387 
388 					for(unsigned int i=0;i<entersector->e->XFloor.ffloors.Size();i++)
389 					{
390 						F3DFloor * rover=entersector->e->XFloor.ffloors[i];
391 						int entershootthrough = !!(rover->flags&FF_SHOOTTHROUGH);
392 
393 						if (entershootthrough != inshootthrough && rover->flags&FF_EXISTS)
394 						{
395 							fixed_t ff_bottom=rover->bottom.plane->ZatPoint(hitx, hity);
396 							fixed_t ff_top=rover->top.plane->ZatPoint(hitx, hity);
397 
398 							// clip to the part of the sector we are in
399 							if (hitz>ff_top)
400 							{
401 								// above
402 								if (bf<ff_top)
403 								{
404 									entersector->floorplane=*rover->top.plane;
405 									entersector->SetTexture(sector_t::floor, *rover->top.texture, false);
406 									bf=ff_top;
407 								}
408 							}
409 							else if (hitz<ff_bottom)
410 							{
411 								//below
412 								if (bc>ff_bottom)
413 								{
414 									entersector->ceilingplane=*rover->bottom.plane;
415 									entersector->SetTexture(sector_t::ceiling, *rover->bottom.texture, false);
416 									bc=ff_bottom;
417 								}
418 							}
419 							else
420 							{
421 								//hit the edge - equivalent to hitting the wall
422 								Results->HitType = TRACE_HitWall;
423 								Results->Tier = TIER_FFloor;
424 								Results->ffloor = rover;
425 								if ((TraceFlags & TRACE_Impact) && in->d.line->special)
426 								{
427 									P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
428 								}
429 								goto cont;
430 							}
431 						}
432 					}
433 				}
434 
435 				Results->HitType = TRACE_HitNone;
436 				if (TraceFlags & TRACE_PCross)
437 				{
438 					P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCross);
439 				}
440 				if (TraceFlags & TRACE_Impact)
441 				{ // This is incorrect for "impact", but Hexen did this, so
442 				  // we need to as well, for compatibility
443 					P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
444 				}
445 			}
446 cont:
447 
448 			if (Results->HitType != TRACE_HitNone)
449 			{
450 				// We hit something, so figure out where exactly
451 				Results->Sector = &sectors[CurSector->sectornum];
452 
453 				if (Results->HitType != TRACE_HitWall &&
454 					!CheckSectorPlane (CurSector, Results->HitType == TRACE_HitFloor))
455 				{ // trace is parallel to the plane (or right on it)
456 					if (entersector == NULL)
457 					{
458 						Results->HitType = TRACE_HitWall;
459 						Results->Tier = TIER_Middle;
460 					}
461 					else
462 					{
463 						if (hitz <= bf || hitz >= bc)
464 						{
465 							Results->HitType = TRACE_HitWall;
466 							Results->Tier =
467 								hitz <= bf ? TIER_Lower :
468 								hitz >= bc ? TIER_Upper : TIER_Middle;
469 						}
470 						else
471 						{
472 							Results->HitType = TRACE_HitNone;
473 						}
474 					}
475 					if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact)
476 					{
477 						P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_Impact);
478 					}
479 				}
480 
481 				if (Results->HitType == TRACE_HitWall)
482 				{
483 					Results->X = hitx;
484 					Results->Y = hity;
485 					Results->Z = hitz;
486 					Results->Distance = dist;
487 					Results->Fraction = in->frac;
488 					Results->Line = in->d.line;
489 					Results->Side = lineside;
490 				}
491 			}
492 
493 			if (Results->HitType == TRACE_HitNone)
494 			{
495 				CurSector = entersector;
496 				EnterDist = dist;
497 				continue;
498 			}
499 
500 			if (TraceCallback != NULL)
501 			{
502 				switch (TraceCallback(*Results, TraceCallbackData))
503 				{
504 				case TRACE_Stop:	return false;
505 				case TRACE_Abort:	Results->HitType = TRACE_HitNone; return false;
506 				case TRACE_Skip:	Results->HitType = TRACE_HitNone; break;
507 				default:			break;
508 				}
509 			}
510 			else
511 			{
512 				return false;
513 			}
514 		}
515 
516 		// Encountered an actor
517 		if (!(in->d.thing->flags & ActorMask) || in->d.thing == IgnoreThis)
518 		{
519 			continue;
520 		}
521 
522 		dist = FixedMul (MaxDist, in->frac);
523 		hitx = StartX + FixedMul (Vx, dist);
524 		hity = StartY + FixedMul (Vy, dist);
525 		hitz = StartZ + FixedMul (Vz, dist);
526 
527 		if (hitz > in->d.thing->Top())
528 		{ // trace enters above actor
529 			if (Vz >= 0) continue;      // Going up: can't hit
530 
531 			// Does it hit the top of the actor?
532 			dist = FixedDiv(in->d.thing->Top() - StartZ, Vz);
533 
534 			if (dist > MaxDist) continue;
535 			in->frac = FixedDiv(dist, MaxDist);
536 
537 			hitx = StartX + FixedMul (Vx, dist);
538 			hity = StartY + FixedMul (Vy, dist);
539 			hitz = StartZ + FixedMul (Vz, dist);
540 
541 			// calculated coordinate is outside the actor's bounding box
542 			if (abs(hitx - in->d.thing->X()) > in->d.thing->radius ||
543 				abs(hity - in->d.thing->Y()) > in->d.thing->radius) continue;
544 		}
545 		else if (hitz < in->d.thing->Z())
546 		{ // trace enters below actor
547 			if (Vz <= 0) continue;      // Going down: can't hit
548 
549 			// Does it hit the bottom of the actor?
550 			dist = FixedDiv(in->d.thing->Z() - StartZ, Vz);
551 			if (dist > MaxDist) continue;
552 			in->frac = FixedDiv(dist, MaxDist);
553 
554 			hitx = StartX + FixedMul (Vx, dist);
555 			hity = StartY + FixedMul (Vy, dist);
556 			hitz = StartZ + FixedMul (Vz, dist);
557 
558 			// calculated coordinate is outside the actor's bounding box
559 			if (abs(hitx - in->d.thing->X()) > in->d.thing->radius ||
560 				abs(hity - in->d.thing->Y()) > in->d.thing->radius) continue;
561 		}
562 
563 		// check for extrafloors first
564 		if (CurSector->e->XFloor.ffloors.Size())
565 		{
566 			fixed_t ff_floor=CurSector->floorplane.ZatPoint(hitx, hity);
567 			fixed_t ff_ceiling=CurSector->ceilingplane.ZatPoint(hitx, hity);
568 
569 			if (hitz>ff_ceiling)	// actor is hit above the current ceiling
570 			{
571 				Results->HitType=TRACE_HitCeiling;
572 				Results->HitTexture = CurSector->GetTexture(sector_t::ceiling);
573 			}
574 			else if (hitz<ff_floor)	// actor is hit below the current floor
575 			{
576 				Results->HitType=TRACE_HitFloor;
577 				Results->HitTexture = CurSector->GetTexture(sector_t::floor);
578 			}
579 			else goto cont1;
580 
581 			// the trace hit a 3D-floor before the thing.
582 			// Calculate an intersection and abort.
583 			Results->Sector = &sectors[CurSector->sectornum];
584 			if (!CheckSectorPlane(CurSector, Results->HitType == TRACE_HitFloor))
585 			{
586 				Results->HitType = TRACE_HitNone;
587 			}
588 			if (TraceCallback != NULL)
589 			{
590 				switch (TraceCallback(*Results, TraceCallbackData))
591 				{
592 				case TRACE_Continue: return true;
593 				case TRACE_Stop:	 return false;
594 				case TRACE_Abort:	 Results->HitType = TRACE_HitNone; return false;
595 				case TRACE_Skip:	 Results->HitType = TRACE_HitNone; return true;
596 				}
597 			}
598 			else
599 			{
600 				return false;
601 			}
602 		}
603 cont1:
604 
605 		Results->HitType = TRACE_HitActor;
606 		Results->X = hitx;
607 		Results->Y = hity;
608 		Results->Z = hitz;
609 		Results->Distance = dist;
610 		Results->Fraction = in->frac;
611 		Results->Actor = in->d.thing;
612 
613 		if (TraceCallback != NULL)
614 		{
615 			switch (TraceCallback(*Results, TraceCallbackData))
616 			{
617 			case TRACE_Stop:	return false;
618 			case TRACE_Abort:	Results->HitType = TRACE_HitNone; return false;
619 			case TRACE_Skip:	Results->HitType = TRACE_HitNone; break;
620 			default:			break;
621 			}
622 		}
623 		else
624 		{
625 			return false;
626 		}
627 	}
628 	return true;
629 }
630 
CheckPlane(const secplane_t & plane)631 bool FTraceInfo::CheckPlane (const secplane_t &plane)
632 {
633 	fixed_t den = TMulScale16 (plane.a, Vx, plane.b, Vy, plane.c, Vz);
634 
635 	if (den != 0)
636 	{
637 		fixed_t num = TMulScale16 (plane.a, StartX,
638 								   plane.b, StartY,
639 								   plane.c, StartZ) + plane.d;
640 
641 		fixed_t hitdist = FixedDiv (-num, den);
642 
643 		if (hitdist > EnterDist && hitdist < MaxDist)
644 		{
645 			Results->X = StartX + FixedMul (Vx, hitdist);
646 			Results->Y = StartY + FixedMul (Vy, hitdist);
647 			Results->Z = StartZ + FixedMul (Vz, hitdist);
648 			Results->Distance = hitdist;
649 			Results->Fraction = FixedDiv (hitdist, MaxDist);
650 			return true;
651 		}
652 	}
653 	return false;
654 }
655 
CheckSectorPlane(const sector_t * sector,bool checkFloor)656 bool FTraceInfo::CheckSectorPlane (const sector_t *sector, bool checkFloor)
657 {
658 	secplane_t plane;
659 
660 	if (checkFloor)
661 	{
662 		plane = sector->floorplane;
663 	}
664 	else
665 	{
666 		plane = sector->ceilingplane;
667 	}
668 
669 	return CheckPlane(plane);
670 }
671 
Check3DFloorPlane(const F3DFloor * ffloor,bool checkBottom)672 bool FTraceInfo::Check3DFloorPlane (const F3DFloor *ffloor, bool checkBottom)
673 {
674 	secplane_t plane;
675 
676 	if (checkBottom)
677 	{
678 		plane = *(ffloor->bottom.plane);
679 	}
680 	else
681 	{
682 		plane = *(ffloor->top.plane);
683 	}
684 
685 	return CheckPlane(plane);
686 }
687 
EditTraceResult(DWORD flags,FTraceResults & res)688 static bool EditTraceResult (DWORD flags, FTraceResults &res)
689 {
690 	if (flags & TRACE_NoSky)
691 	{ // Throw away sky hits
692 		if (res.HitType == TRACE_HitFloor || res.HitType == TRACE_HitCeiling)
693 		{
694 			if (res.HitTexture == skyflatnum)
695 			{
696 				res.HitType = TRACE_HitNone;
697 				return false;
698 			}
699 		}
700 		else if (res.HitType == TRACE_HitWall)
701 		{
702 			if (res.Tier == TIER_Upper &&
703 				res.Line->frontsector->GetTexture(sector_t::ceiling) == skyflatnum &&
704 				res.Line->backsector->GetTexture(sector_t::ceiling) == skyflatnum)
705 			{
706 				res.HitType = TRACE_HitNone;
707 				return false;
708 			}
709 		}
710 	}
711 	return true;
712 }
713