1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: p_entity_world.cpp 4361 2010-12-31 00:59:19Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 //**
26 //**	VEntity collision, physics and related methods.
27 //**
28 //**************************************************************************
29 
30 // HEADER FILES ------------------------------------------------------------
31 
32 #include "gamedefs.h"
33 #include "sv_local.h"
34 
35 // MACROS ------------------------------------------------------------------
36 
37 #define WATER_SINK_FACTOR		3.0
38 #define WATER_SINK_SPEED		0.5
39 
40 // TYPES -------------------------------------------------------------------
41 
42 struct cptrace_t
43 {
44 	TVec Pos;
45 	float bbox[4];
46 	float FloorZ;
47 	float CeilingZ;
48 	float DropOffZ;
49 	sec_plane_t *Floor;
50 	sec_plane_t *Ceiling;
51 };
52 
53 struct tmtrace_t
54 {
55 	VEntity* StepThing;
56 	TVec End;
57 	float BBox[4];
58 	float FloorZ;
59 	float CeilingZ;
60 	float DropOffZ;
61 	sec_plane_t *Floor;
62 	sec_plane_t *Ceiling;
63 
64 	enum
65 	{
66 		TF_FloatOk = 0x01,	// if true, move would be ok if
67 							// within tmtrace.FloorZ - tmtrace.CeilingZ
68 	};
69 	vuint32 TraceFlags;
70 
71 	// keep track of the line that lowers the ceiling,
72 	// so missiles don't explode against sky hack walls
73 	line_t *CeilingLine;
74 	// also keep track of the blocking line, for checking
75 	// against doortracks
76 	line_t *BlockingLine;
77 
78 	// keep track of special lines as they are hit,
79 	// but don't process them until the move is proven valid
80 	TArray<line_t*>		SpecHit;
81 
82 	VEntity *BlockingMobj;
83 };
84 
85 //	Searches though the surrounding mapblocks for monsters/players
86 //      distance is in MAPBLOCKUNITS
87 class VRoughBlockSearchIterator : public VScriptIterator
88 {
89 private:
90 	VEntity*	Self;
91 	int			Distance;
92 	VEntity*	Ent;
93 	VEntity**	EntPtr;
94 
95 	int			StartX;
96 	int			StartY;
97 	int			Count;
98 	int			CurrentEdge;
99 	int			BlockIndex;
100 	int			FirstStop;
101 	int			SecondStop;
102 	int			ThirdStop;
103 	int			FinalStop;
104 
105 public:
106 	VRoughBlockSearchIterator(VEntity*, int, VEntity**);
107 	bool GetNext();
108 };
109 
110 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
111 
112 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
113 
114 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
115 
116 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
117 
118 extern VCvarI	compat_nopassover;
119 
120 // PUBLIC DATA DEFINITIONS -------------------------------------------------
121 
122 // PRIVATE DATA DEFINITIONS ------------------------------------------------
123 
124 // CODE --------------------------------------------------------------------
125 
126 //**************************************************************************
127 //
128 //	THING POSITION SETTING
129 //
130 //**************************************************************************
131 
132 //=============================================================================
133 //
134 //	VEntity::CreateSecNodeList
135 //
136 // phares 3/14/98
137 //
138 //	Alters/creates the sector_list that shows what sectors the object resides in
139 //
140 //=============================================================================
141 
CreateSecNodeList()142 void VEntity::CreateSecNodeList()
143 {
144 	guard(VEntity::CreateSecNodeList);
145 	int xl, xh, yl, yh, bx, by;
146 	msecnode_t* Node;
147 
148 	// First, clear out the existing Thing fields. As each node is
149 	// added or verified as needed, Thing will be set properly. When
150 	// finished, delete all nodes where Thing is still NULL. These
151 	// represent the sectors the Thing has vacated.
152 
153 	Node = XLevel->SectorList;
154 	while (Node)
155 	{
156 		Node->Thing = NULL;
157 		Node = Node->TNext;
158 	}
159 
160 	float tmbbox[4];
161 	tmbbox[BOXTOP] = Origin.y + Radius;
162 	tmbbox[BOXBOTTOM] = Origin.y - Radius;
163 	tmbbox[BOXRIGHT] = Origin.x + Radius;
164 	tmbbox[BOXLEFT] = Origin.x - Radius;
165 
166 	validcount++; // used to make sure we only process a line once
167 
168 	xl = MapBlock(tmbbox[BOXLEFT] - XLevel->BlockMapOrgX);
169 	xh = MapBlock(tmbbox[BOXRIGHT] - XLevel->BlockMapOrgX);
170 	yl = MapBlock(tmbbox[BOXBOTTOM] - XLevel->BlockMapOrgY);
171 	yh = MapBlock(tmbbox[BOXTOP] - XLevel->BlockMapOrgY);
172 
173 	for (bx = xl; bx <= xh; bx++)
174 	{
175 		for (by = yl; by <= yh; by++)
176 		{
177 			line_t* ld;
178 			for (VBlockLinesIterator It(XLevel, bx, by, &ld); It.GetNext(); )
179 			{
180 				//	Locates all the sectors the object is in by looking at
181 				// the lines that cross through it. You have already decided
182 				// that the object is allowed at this location, so don't
183 				// bother with checking impassable or blocking lines.
184 				if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
185 					tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
186 					tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
187 					tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
188 				{
189 					continue;
190 				}
191 
192 				if (P_BoxOnLineSide(tmbbox, ld) != -1)
193 				{
194 					continue;
195 				}
196 
197 				// This line crosses through the object.
198 
199 				// Collect the sector(s) from the line and add to the
200 				// SectorList you're examining. If the Thing ends up being
201 				// allowed to move to this position, then the sector_list will
202 				// be attached to the Thing's VEntity at TouchingSectorList.
203 
204 				XLevel->SectorList = XLevel->AddSecnode(ld->frontsector,
205 					this, XLevel->SectorList);
206 
207 				// Don't assume all lines are 2-sided, since some Things like
208 				// MT_TFOG are allowed regardless of whether their radius
209 				// takes them beyond an impassable linedef.
210 
211 				// killough 3/27/98, 4/4/98:
212 				// Use sidedefs instead of 2s flag to determine two-sidedness.
213 
214 				if (ld->backsector)
215 				{
216 					XLevel->SectorList = XLevel->AddSecnode(ld->backsector,
217 						this, XLevel->SectorList);
218 				}
219 			}
220 		}
221 	}
222 
223 	// Add the sector of the (x,y) point to sector_list.
224 	XLevel->SectorList = XLevel->AddSecnode(Sector, this, XLevel->SectorList);
225 
226 	// Now delete any nodes that won't be used. These are the ones where
227 	// Thing is still NULL.
228 
229 	Node = XLevel->SectorList;
230 	while (Node)
231 	{
232 		if (Node->Thing == NULL)
233 		{
234 			if (Node == XLevel->SectorList)
235 			{
236 				XLevel->SectorList = Node->TNext;
237 			}
238 			Node = XLevel->DelSecnode(Node);
239 		}
240 		else
241 		{
242 			Node = Node->TNext;
243 		}
244 	}
245 	unguard;
246 }
247 
248 //==========================================================================
249 //
250 //	VEntity::UnlinkFromWorld
251 //
252 //	Unlinks a thing from block map and sectors. On each position change,
253 // BLOCKMAP and other lookups maintaining lists ot things inside these
254 // structures need to be updated.
255 //
256 //==========================================================================
257 
UnlinkFromWorld()258 void VEntity::UnlinkFromWorld()
259 {
260 	guard(SV_UnlinkFromWorld);
261 	if (!SubSector)
262 	{
263 		return;
264 	}
265 
266 	if (!(EntityFlags & EF_NoSector))
267 	{
268 		// invisible things don't need to be in sector list
269 		// unlink from subsector
270 		if (SNext)
271 		{
272 			SNext->SPrev = SPrev;
273 		}
274 		if (SPrev)
275 		{
276 			SPrev->SNext = SNext;
277 		}
278 		else
279 		{
280 			Sector->ThingList = SNext;
281 		}
282 		SNext = NULL;
283 		SPrev = NULL;
284 
285 		// phares 3/14/98
286 		//
287 		//	Save the sector list pointed to by TouchingSectorList. In
288 		// LinkToWorld, we'll keep any nodes that represent sectors the Thing
289 		// still touches. We'll add new ones then, and delete any nodes for
290 		// sectors the Thing has vacated. Then we'll put it back into
291 		// TouchingSectorList. It's done this way to avoid a lot of
292 		// deleting/creating for nodes, when most of the time you just get
293 		// back what you deleted anyway.
294 		//
295 		//	If this Thing is being removed entirely, then the calling routine
296 		// will clear out the nodes in sector_list.
297 		//
298 		XLevel->DelSectorList();
299 		XLevel->SectorList = TouchingSectorList;
300 		TouchingSectorList = NULL; //to be restored by LinkToWorld
301 	}
302 
303 	if (!(EntityFlags & EF_NoBlockmap))
304 	{
305 		//	Inert things don't need to be in blockmap
306 		//	Unlink from block map
307 		if (BlockMapNext)
308 		{
309 			BlockMapNext->BlockMapPrev = BlockMapPrev;
310 		}
311 
312 		if (BlockMapPrev)
313 		{
314 			BlockMapPrev->BlockMapNext = BlockMapNext;
315 		}
316 		else
317 		{
318 			int blockx = MapBlock(Origin.x - XLevel->BlockMapOrgX);
319 			int blocky = MapBlock(Origin.y - XLevel->BlockMapOrgY);
320 
321 			if (blockx >= 0 && blockx < XLevel->BlockMapWidth &&
322 				blocky >= 0 && blocky < XLevel->BlockMapHeight)
323 			{
324 //				check(XLevel->BlockLinks[blocky * XLevel->BlockMapWidth + blockx] == this);
325 				if (XLevel->BlockLinks[blocky * XLevel->BlockMapWidth + blockx] == this)
326 				{
327 					XLevel->BlockLinks[blocky * XLevel->BlockMapWidth + blockx] =
328 						BlockMapNext;
329 				}
330 			}
331 		}
332 	}
333 	SubSector = NULL;
334 	Sector = NULL;
335 	unguardf(("(%s)", GetClass()->GetName()));
336 }
337 
338 //==========================================================================
339 //
340 //	VEntity::LinkToWorld
341 //
342 //	Links a thing into both a block and a subsector based on it's x y.
343 //	Sets thing->subsector properly
344 //
345 //==========================================================================
346 
LinkToWorld()347 void VEntity::LinkToWorld()
348 {
349 	guard(VEntity::LinkToWorld);
350 	subsector_t*	ss;
351 	sec_region_t*	reg;
352 	sec_region_t*	r;
353 
354 	if (SubSector)
355 	{
356 		UnlinkFromWorld();
357 	}
358 
359 	// link into subsector
360 	ss = XLevel->PointInSubsector(Origin);
361 	reg = SV_FindThingGap(ss->sector->botregion, Origin,
362 		Origin.z, Origin.z + Height);
363 	SubSector = ss;
364 	Sector = ss->sector;
365 
366 	r = reg;
367 	while (r->floor->flags && r->prev)
368 	{
369 		r = r->prev;
370 	}
371 	Floor = r->floor;
372 	FloorZ = r->floor->GetPointZ(Origin);
373 
374 	r = reg;
375 	while (r->ceiling->flags && r->next)
376 	{
377 		r = r->next;
378 	}
379 	Ceiling = r->ceiling;
380 	CeilingZ = r->ceiling->GetPointZ(Origin);
381 
382 	// link into sector
383 	if (!(EntityFlags & EF_NoSector))
384 	{
385 		// invisible things don't go into the sector links
386 		VEntity** Link = &Sector->ThingList;
387 		SPrev = NULL;
388 		SNext = *Link;
389 		if (*Link)
390 		{
391 			(*Link)->SPrev = this;
392 		}
393 		*Link = this;
394 
395 		// phares 3/16/98
396 		//
397 		// If sector_list isn't NULL, it has a collection of sector
398 		// nodes that were just removed from this Thing.
399 		//
400 		// Collect the sectors the object will live in by looking at
401 		// the existing sector_list and adding new nodes and deleting
402 		// obsolete ones.
403 		//
404 		// When a node is deleted, its sector links (the links starting
405 		// at sector_t->touching_thinglist) are broken. When a node is
406 		// added, new sector links are created.
407 		CreateSecNodeList();
408 		TouchingSectorList = XLevel->SectorList;	// Attach to thing
409 		XLevel->SectorList = NULL;		// clear for next time
410 	}
411 	else
412 	{
413 		XLevel->DelSectorList();
414 	}
415 
416 	// link into blockmap
417 	if (!(EntityFlags & EF_NoBlockmap))
418 	{
419 		// inert things don't need to be in blockmap
420 		int blockx = MapBlock(Origin.x - XLevel->BlockMapOrgX);
421 		int blocky = MapBlock(Origin.y - XLevel->BlockMapOrgY);
422 
423 		if (blockx >= 0 && blockx < XLevel->BlockMapWidth &&
424 			blocky >= 0 && blocky < XLevel->BlockMapHeight)
425 		{
426 			VEntity** link = &XLevel->BlockLinks[
427 				blocky * XLevel->BlockMapWidth + blockx];
428 			BlockMapPrev = NULL;
429 			BlockMapNext = *link;
430 			if (*link)
431 			{
432 				(*link)->BlockMapPrev = this;
433 			}
434 
435 			*link = this;
436 		}
437 		else
438 		{
439 			// thing is off the map
440 			BlockMapNext = BlockMapPrev = NULL;
441 		}
442 	}
443 	unguard;
444 }
445 
446 //==========================================================================
447 //
448 //  VEntity::CheckWater
449 //
450 //==========================================================================
451 
CheckWater()452 bool VEntity::CheckWater()
453 {
454 	guard(VEntity::CheckWater);
455 	TVec point;
456 	int cont;
457 
458 	point = Origin;
459 	point.z += 1.0;
460 
461 	WaterLevel = 0;
462 	WaterType = 0;
463 	cont = SV_PointContents(Sector, point);
464 	if (cont > 0)
465 	{
466 		WaterType = cont;
467 		WaterLevel = 1;
468 		point.z = Origin.z + Height * 0.5;
469 		cont = SV_PointContents(Sector, point);
470 		if (cont > 0)
471 		{
472 			WaterLevel = 2;
473 			if (EntityFlags & EF_IsPlayer)
474 			{
475 				point = Player->ViewOrg;
476 				cont = SV_PointContents(Sector, point);
477 				if (cont > 0)
478 				{
479 					WaterLevel = 3;
480 				}
481 			}
482 		}
483 	}
484 	return WaterLevel > 1;
485 	unguard;
486 }
487 
488 //**************************************************************************
489 //
490 //	CHECK ABSOLUTE POSITION
491 //
492 //**************************************************************************
493 
494 //==========================================================================
495 //
496 //  VEntity::CheckPosition
497 //
498 //  This is purely informative, nothing is modified
499 //
500 // in:
501 //  a mobj_t (can be valid or invalid)
502 //  a position to be checked
503 //   (doesn't need to be related to the mobj_t->x,y)
504 //
505 //==========================================================================
506 
CheckPosition(TVec Pos)507 bool VEntity::CheckPosition(TVec Pos)
508 {
509 	guard(VEntity::CheckPosition);
510 	int xl;
511 	int xh;
512 	int yl;
513 	int yh;
514 	int bx;
515 	int by;
516 	subsector_t *newsubsec;
517 	sec_region_t *gap;
518 	sec_region_t *reg;
519 	cptrace_t cptrace;
520 
521 	cptrace.Pos = Pos;
522 
523 	cptrace.bbox[BOXTOP] = Pos.y + Radius;
524 	cptrace.bbox[BOXBOTTOM] = Pos.y - Radius;
525 	cptrace.bbox[BOXRIGHT] = Pos.x + Radius;
526 	cptrace.bbox[BOXLEFT] = Pos.x - Radius;
527 
528 	newsubsec = XLevel->PointInSubsector(Pos);
529 
530 	// The base floor / ceiling is from the subsector that contains the point.
531 	// Any contacted lines the step closer together will adjust them.
532 	gap = SV_FindThingGap(newsubsec->sector->botregion, Pos,
533 		Pos.z, Pos.z + Height);
534 	reg = gap;
535 	while (reg->prev && reg->floor->flags & SPF_NOBLOCKING)
536 	{
537 		reg = reg->prev;
538 	}
539 	cptrace.Floor = reg->floor;
540 	cptrace.FloorZ = reg->floor->GetPointZ(Pos);
541 	cptrace.DropOffZ = cptrace.FloorZ;
542 	reg = gap;
543 	while (reg->next && reg->ceiling->flags & SPF_NOBLOCKING)
544 	{
545 		reg = reg->next;
546 	}
547 	cptrace.Ceiling = reg->ceiling;
548 	cptrace.CeilingZ = reg->ceiling->GetPointZ(Pos);
549 
550 	validcount++;
551 
552 	if (EntityFlags & EF_ColideWithThings)
553 	{
554 		// Check things first, possibly picking things up.
555 		// The bounding box is extended by MAXRADIUS
556 		// because mobj_ts are grouped into mapblocks
557 		// based on their origin point, and can overlap
558 		// into adjacent blocks by up to MAXRADIUS units.
559 		xl = MapBlock(cptrace.bbox[BOXLEFT] - XLevel->BlockMapOrgX - MAXRADIUS);
560 		xh = MapBlock(cptrace.bbox[BOXRIGHT] - XLevel->BlockMapOrgX + MAXRADIUS);
561 		yl = MapBlock(cptrace.bbox[BOXBOTTOM] - XLevel->BlockMapOrgY - MAXRADIUS);
562 		yh = MapBlock(cptrace.bbox[BOXTOP] - XLevel->BlockMapOrgY + MAXRADIUS);
563 
564 		for (bx = xl; bx <= xh; bx++)
565 		{
566 			for (by = yl; by <= yh; by++)
567 			{
568 				for (VBlockThingsIterator It(XLevel, bx, by); It; ++It)
569 				{
570 					if (!CheckThing(cptrace, *It))
571 					{
572 						return false;
573 					}
574 				}
575 			}
576 		}
577 	}
578 
579 	if (EntityFlags & EF_ColideWithWorld)
580 	{
581 		// check lines
582 		xl = MapBlock(cptrace.bbox[BOXLEFT] - XLevel->BlockMapOrgX);
583 		xh = MapBlock(cptrace.bbox[BOXRIGHT] - XLevel->BlockMapOrgX);
584 		yl = MapBlock(cptrace.bbox[BOXBOTTOM] - XLevel->BlockMapOrgY);
585 		yh = MapBlock(cptrace.bbox[BOXTOP] - XLevel->BlockMapOrgY);
586 
587 		for (bx = xl; bx <= xh; bx++)
588 		{
589 			for (by = yl; by <= yh; by++)
590 			{
591 				line_t*		ld;
592 				for (VBlockLinesIterator It(XLevel, bx, by, &ld); It.GetNext(); )
593 				{
594 					if (!CheckLine(cptrace, ld))
595 					{
596 						return false;
597 					}
598 				}
599 			}
600 		}
601 	}
602 
603 	return true;
604 	unguard;
605 }
606 
607 //==========================================================================
608 //
609 //	VEntity::CheckThing
610 //
611 //==========================================================================
612 
CheckThing(cptrace_t & cptrace,VEntity * Other)613 bool VEntity::CheckThing(cptrace_t& cptrace, VEntity *Other)
614 {
615 	guardSlow(VEntity::CheckThing);
616 	// can't hit thing
617 	if (!(Other->EntityFlags & EF_Solid))
618 	{
619 		return true;
620 	}
621 
622 	// don't clip against self
623 	if (Other == this)
624 	{
625 		return true;
626 	}
627 
628 	float blockdist = Other->Radius + Radius;
629 
630 	if (fabs(Other->Origin.x - cptrace.Pos.x) >= blockdist ||
631 		fabs(Other->Origin.y - cptrace.Pos.y) >= blockdist)
632 	{
633 		// didn't hit it
634 		return true;
635 	}
636 
637 	if ((EntityFlags & EF_PassMobj) || (EntityFlags & EF_Missile) ||
638 		(Other->EntityFlags & EF_ActLikeBridge))
639 	{
640 		//	Prevent some objects from overlapping
641 		if (EntityFlags & Other->EntityFlags & EF_DontOverlap)
642 		{
643 			return false;
644 		}
645 		// check if a mobj passed over/under another object
646 		if (cptrace.Pos.z >= Other->Origin.z + Other->Height)
647 		{
648 			return true;
649 		}
650 		if (cptrace.Pos.z + Height < Other->Origin.z)
651 		{
652 			// under thing
653 			return true;
654 		}
655 	}
656 
657 	return false;
658 	unguardSlow;
659 }
660 
661 //==========================================================================
662 //
663 //	VEntity::CheckLine
664 //
665 //  Adjusts cptrace.FloorZ and cptrace.CeilingZ as lines are contacted
666 //
667 //==========================================================================
668 
CheckLine(cptrace_t & cptrace,line_t * ld)669 bool VEntity::CheckLine(cptrace_t& cptrace, line_t* ld)
670 {
671 	guardSlow(VEntity::CheckLine);
672 	if (cptrace.bbox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
673 		cptrace.bbox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
674 		cptrace.bbox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
675 		cptrace.bbox[BOXBOTTOM] >= ld->bbox[BOXTOP])
676 	{
677 		return true;
678 	}
679 
680 	if (P_BoxOnLineSide(&cptrace.bbox[0], ld) != -1)
681 	{
682 		return true;
683 	}
684 
685 	// A line has been hit
686 	if (!ld->backsector)
687 	{
688 		// One sided line
689 		return false;
690 	}
691 
692 	if (!(ld->flags & ML_RAILING))
693 	{
694 		if (ld->flags & ML_BLOCKEVERYTHING)
695 		{
696 			// Explicitly blocking everything
697 			return false;
698 		}
699 
700 		if ((EntityFlags & VEntity::EF_CheckLineBlocking) &&
701 			(ld->flags & ML_BLOCKING))
702 		{
703 			// Explicitly blocking everything
704 			return false;
705 		}
706 
707 		if ((EntityFlags & VEntity::EF_CheckLineBlockMonsters) &&
708 			(ld->flags & ML_BLOCKMONSTERS))
709 		{
710 			// Block monsters only
711 			return false;
712 		}
713 
714 		if ((EntityFlags & VEntity::EF_IsPlayer) &&
715 			(ld->flags & ML_BLOCKPLAYERS))
716 		{
717 			// Block players only
718 			return false;
719 		}
720 
721 		if ((EntityFlags & VEntity::EF_Float) &&
722 			(ld->flags & ML_BLOCK_FLOATERS))
723 		{
724 			// Block floaters only
725 			return false;
726 		}
727 	}
728 
729 	// set openrange, opentop, openbottom
730 	TVec hit_point = cptrace.Pos - (DotProduct(cptrace.Pos, ld->normal) -
731 		ld->dist) * ld->normal;
732 	opening_t* open = SV_LineOpenings(ld, hit_point, SPF_NOBLOCKING);
733 	open = SV_FindOpening(open, cptrace.Pos.z, cptrace.Pos.z + Height);
734 
735 	if (open)
736 	{
737 		// adjust floor / ceiling heights
738 		if (!(open->ceiling->flags & SPF_NOBLOCKING)
739 			&& open->top < cptrace.CeilingZ)
740 		{
741 			cptrace.Ceiling = open->ceiling;
742 			cptrace.CeilingZ = open->top;
743 		}
744 
745 		if (!(open->floor->flags & SPF_NOBLOCKING) && open->bottom > cptrace.FloorZ)
746 		{
747 			cptrace.Floor = open->floor;
748 			cptrace.FloorZ = open->bottom;
749 		}
750 
751 		if (open->lowfloor < cptrace.DropOffZ)
752 		{
753 			cptrace.DropOffZ = open->lowfloor;
754 		}
755 
756 		if (ld->flags & ML_RAILING)
757 		{
758 			cptrace.FloorZ += 32;
759 		}
760 	}
761 	else
762 	{
763 		cptrace.CeilingZ = cptrace.FloorZ;
764 	}
765 
766 	return true;
767 	unguardSlow;
768 }
769 
770 //**************************************************************************
771 //
772 //  MOVEMENT CLIPPING
773 //
774 //**************************************************************************
775 
776 //==========================================================================
777 //
778 //  VEntity::CheckRelPosition
779 //
780 //  This is purely informative, nothing is modified
781 // (except things picked up).
782 //
783 // in:
784 //  a mobj_t (can be valid or invalid)
785 //  a position to be checked
786 //   (doesn't need to be related to the mobj_t->x,y)
787 //
788 // during:
789 //  special things are touched if MF_PICKUP
790 //  early out on solid lines?
791 //
792 // out:
793 //  newsubsec
794 //  floorz
795 //  ceilingz
796 //  tmdropoffz
797 //   the lowest point contacted
798 //   (monsters won't move to a dropoff)
799 //  speciallines[]
800 //  numspeciallines
801 //  VEntity *BlockingMobj = pointer to thing that blocked position (NULL if not
802 //   blocked, or blocked by a line).
803 //
804 //==========================================================================
805 
CheckRelPosition(tmtrace_t & tmtrace,TVec Pos)806 bool VEntity::CheckRelPosition(tmtrace_t& tmtrace, TVec Pos)
807 {
808 	guard(VEntity::CheckRelPosition);
809 	int xl;
810 	int xh;
811 	int yl;
812 	int yh;
813 	int bx;
814 	int by;
815 	subsector_t *newsubsec;
816 	VEntity* thingblocker;
817 	VEntity* fakedblocker;
818 
819 	tmtrace.End = Pos;
820 
821 	tmtrace.BBox[BOXTOP] = Pos.y + Radius;
822 	tmtrace.BBox[BOXBOTTOM] = Pos.y - Radius;
823 	tmtrace.BBox[BOXRIGHT] = Pos.x + Radius;
824 	tmtrace.BBox[BOXLEFT] = Pos.x - Radius;
825 
826 	newsubsec = XLevel->PointInSubsector(Pos);
827 	tmtrace.CeilingLine = NULL;
828 
829 	// The base floor / ceiling is from the subsector
830 	// that contains the point.
831 	// Any contacted lines the step closer together
832 	// will adjust them.
833 	if (newsubsec->sector->SectorFlags && sector_t::SF_HasExtrafloors)
834 	{
835 		sec_region_t* gap = SV_FindThingGap(newsubsec->sector->botregion,
836 			tmtrace.End, tmtrace.End.z, tmtrace.End.z + Height);
837 		sec_region_t* reg = gap;
838 		while (reg->prev && reg->floor->flags & SPF_NOBLOCKING)
839 		{
840 			reg = reg->prev;
841 		}
842 		tmtrace.Floor = reg->floor;
843 		tmtrace.FloorZ = reg->floor->GetPointZ(tmtrace.End);
844 		tmtrace.DropOffZ = tmtrace.FloorZ;
845 		reg = gap;
846 		while (reg->next && reg->ceiling->flags & SPF_NOBLOCKING)
847 		{
848 			reg = reg->next;
849 		}
850 		tmtrace.Ceiling = reg->ceiling;
851 		tmtrace.CeilingZ = reg->ceiling->GetPointZ(tmtrace.End);
852 	}
853 	else
854 	{
855 		sec_region_t* reg = newsubsec->sector->botregion;
856 		tmtrace.Floor = reg->floor;
857 		tmtrace.FloorZ = reg->floor->GetPointZ(tmtrace.End);
858 		tmtrace.DropOffZ = tmtrace.FloorZ;
859 		tmtrace.Ceiling = reg->ceiling;
860 		tmtrace.CeilingZ = reg->ceiling->GetPointZ(tmtrace.End);
861 	}
862 
863 	validcount++;
864 	tmtrace.SpecHit.Clear();
865 
866 	tmtrace.BlockingMobj = NULL;
867 	tmtrace.StepThing = NULL;
868 	thingblocker = NULL;
869 	fakedblocker = NULL;
870 
871 	// Check things first, possibly picking things up.
872 	// The bounding box is extended by MAXRADIUS
873 	// because mobj_ts are grouped into mapblocks
874 	// based on their origin point, and can overlap
875 	// into adjacent blocks by up to MAXRADIUS units.
876 	if (EntityFlags & EF_ColideWithThings)
877 	{
878 		xl = MapBlock(tmtrace.BBox[BOXLEFT] - XLevel->BlockMapOrgX - MAXRADIUS);
879 		xh = MapBlock(tmtrace.BBox[BOXRIGHT] - XLevel->BlockMapOrgX + MAXRADIUS);
880 		yl = MapBlock(tmtrace.BBox[BOXBOTTOM] - XLevel->BlockMapOrgY - MAXRADIUS);
881 		yh = MapBlock(tmtrace.BBox[BOXTOP] - XLevel->BlockMapOrgY + MAXRADIUS);
882 
883 		for (bx = xl; bx <= xh; bx++)
884 		{
885 			for (by = yl; by <= yh; by++)
886 			{
887 				for (VBlockThingsIterator It(XLevel, bx, by); It; ++It)
888 				{
889 					if (!CheckRelThing(tmtrace, *It))
890 					{
891 						// continue checking for other things in to see if we hit something
892 						if (!tmtrace.BlockingMobj || compat_nopassover ||
893 							(Level->LevelInfoFlags2 & VLevelInfo::LIF2_CompatNoPassOver))
894 						{
895 							// slammed into something
896 							return false;
897 						}
898 						else if (!tmtrace.BlockingMobj->Player &&
899 							!(EntityFlags & VEntity::EF_Float) &&
900 							!(EntityFlags & VEntity::EF_Missile) &&
901 							tmtrace.BlockingMobj->Origin.z + tmtrace.BlockingMobj->Height - Origin.z <= MaxStepHeight)
902 						{
903 							if (!thingblocker || tmtrace.BlockingMobj->Origin.z > thingblocker->Origin.z)
904 							{
905 								thingblocker = tmtrace.BlockingMobj;
906 							}
907 							tmtrace.BlockingMobj = NULL;
908 						}
909 						else if (Player && Origin.z + Height - tmtrace.BlockingMobj->Origin.z <= MaxStepHeight)
910 						{
911 							if (thingblocker)
912 							{ // something to step up on, set it as
913 							  // the blocker so that we don't step up
914 								return false;
915 							}
916 							// nothing is blocking, but this object potentially could
917 							// if there is something else to step on
918 							fakedblocker = tmtrace.BlockingMobj;
919 							tmtrace.BlockingMobj = NULL;
920 						}
921 						else
922 						{ // blocking
923 							return false;
924 						}
925 					}
926 				}
927 			}
928 		}
929 	}
930 
931 	float thingdropoffz = tmtrace.FloorZ;
932 	tmtrace.FloorZ = tmtrace.DropOffZ;
933 	tmtrace.BlockingMobj = NULL;
934 	// check lines
935 	validcount++;
936 
937 	if (EntityFlags & EF_ColideWithWorld)
938 	{
939 		xl = MapBlock(tmtrace.BBox[BOXLEFT] - XLevel->BlockMapOrgX);
940 		xh = MapBlock(tmtrace.BBox[BOXRIGHT] - XLevel->BlockMapOrgX);
941 		yl = MapBlock(tmtrace.BBox[BOXBOTTOM] - XLevel->BlockMapOrgY);
942 		yh = MapBlock(tmtrace.BBox[BOXTOP] - XLevel->BlockMapOrgY);
943 
944 		for (bx = xl; bx <= xh; bx++)
945 		{
946 			for (by = yl; by <= yh; by++)
947 			{
948 				line_t*		ld;
949 				for (VBlockLinesIterator It(XLevel, bx, by, &ld); It.GetNext(); )
950 				{
951 					if (!CheckRelLine(tmtrace, ld))
952 					{
953 						return false;
954 					}
955 				}
956 			}
957 		}
958 
959 		if (tmtrace.CeilingZ - tmtrace.FloorZ < Height)
960 		{
961 			return false;
962 		}
963 	}
964 
965 	if (tmtrace.StepThing != NULL)
966 	{
967 		tmtrace.DropOffZ = thingdropoffz;
968 	}
969 
970 	tmtrace.BlockingMobj = thingblocker;
971 	if (tmtrace.BlockingMobj)
972 	{
973 		return false;
974 	}
975 
976 	return true;
977 	unguard;
978 }
979 
980 //==========================================================================
981 //
982 //	VEntity::CheckRelThing
983 //
984 //==========================================================================
985 
CheckRelThing(tmtrace_t & tmtrace,VEntity * Other)986 bool VEntity::CheckRelThing(tmtrace_t& tmtrace, VEntity *Other)
987 {
988 	guardSlow(VEntity::CheckRelThing);
989 	// don't clip against self
990 	if (Other == this)
991 	{
992 		return true;
993 	}
994 
995 	float blockdist = Other->Radius + Radius;
996 
997 	if (fabs(Other->Origin.x - tmtrace.End.x) >= blockdist ||
998 		fabs(Other->Origin.y - tmtrace.End.y) >= blockdist)
999 	{
1000 		// didn't hit it
1001 		return true;
1002 	}
1003 
1004 	tmtrace.BlockingMobj = Other;
1005 	if (!(Level->LevelInfoFlags2 & VLevelInfo::LIF2_CompatNoPassOver) &&
1006 		!compat_nopassover &&
1007 		(!(EntityFlags & EF_Float) ||
1008 		!(EntityFlags & EF_Missile) ||
1009 		!(EntityFlags & EF_NoGravity)) &&
1010 		(Other->EntityFlags & EF_Solid) &&
1011 		(Other->EntityFlags & EF_ActLikeBridge))
1012 	{
1013 		// allow actors to walk on other actors as well as floors
1014 		if (Other->Origin.z + Other->Height >= tmtrace.FloorZ &&
1015 			Other->Origin.z + Other->Height <= Origin.z + MaxStepHeight)
1016 		{
1017 			tmtrace.StepThing = Other;
1018 			tmtrace.FloorZ = Other->Origin.z + Other->Height;
1019 		}
1020 	}
1021 	//if (!(tmtrace.Thing->EntityFlags & VEntity::EF_NoPassMobj) || Actor(Other).bSpecial)
1022 	if ((((EntityFlags & EF_PassMobj) ||
1023 		(Other->EntityFlags & EF_ActLikeBridge)) &&
1024 		!(Level->LevelInfoFlags2 & VLevelInfo::LIF2_CompatNoPassOver) &&
1025 		!compat_nopassover) || (EntityFlags & EF_Missile))
1026 	{
1027 		//	Prevent some objects from overlapping
1028 		if (EntityFlags & Other->EntityFlags & EF_DontOverlap)
1029 		{
1030 			return false;
1031 		}
1032 		// check if a mobj passed over/under another object
1033 		if (tmtrace.End.z + 0.00001 >= Other->Origin.z + Other->Height)
1034 		{
1035 			return true;	// overhead
1036 		}
1037 		if (tmtrace.End.z + Height <= Other->Origin.z + 0.00001)
1038 		{
1039 			return true;	// underneath
1040 		}
1041 	}
1042 
1043 	return eventTouch(Other);
1044 	unguardSlow;
1045 }
1046 
1047 //==========================================================================
1048 //
1049 //	VEntity::CheckRelLine
1050 //
1051 //  Adjusts tmtrace.FloorZ and tmtrace.CeilingZ as lines are contacted
1052 //
1053 //==========================================================================
1054 
CheckRelLine(tmtrace_t & tmtrace,line_t * ld)1055 bool VEntity::CheckRelLine(tmtrace_t& tmtrace, line_t* ld)
1056 {
1057 	guardSlow(VEntity::CheckRelLine);
1058 	if (tmtrace.BBox[BOXRIGHT] <= ld->bbox[BOXLEFT] ||
1059 		tmtrace.BBox[BOXLEFT] >= ld->bbox[BOXRIGHT] ||
1060 		tmtrace.BBox[BOXTOP] <= ld->bbox[BOXBOTTOM] ||
1061 		tmtrace.BBox[BOXBOTTOM] >= ld->bbox[BOXTOP])
1062 	{
1063 		return true;
1064 	}
1065 
1066 	if (P_BoxOnLineSide(&tmtrace.BBox[0], ld) != -1)
1067 	{
1068 		return true;
1069 	}
1070 
1071 	// A line has been hit
1072 
1073 	// The moving thing's destination position will cross
1074 	// the given line.
1075 	// If this should not be allowed, return false.
1076 	// If the line is special, keep track of it
1077 	// to process later if the move is proven ok.
1078 	// NOTE: specials are NOT sorted by order,
1079 	// so two special lines that are only 8 pixels apart
1080 	// could be crossed in either order.
1081 
1082 	if (!ld->backsector)
1083 	{
1084 		// One sided line
1085 		BlockedByLine(ld);
1086 		// mark the line as blocking line
1087 		tmtrace.BlockingLine = ld;
1088 		return false;
1089 	}
1090 
1091 	if (!(ld->flags & ML_RAILING))
1092 	{
1093 		if (ld->flags & ML_BLOCKEVERYTHING)
1094 		{
1095 			// Explicitly blocking everything
1096 			BlockedByLine(ld);
1097 			return false;
1098 		}
1099 
1100 		if ((EntityFlags & VEntity::EF_CheckLineBlocking) &&
1101 			(ld->flags & ML_BLOCKING))
1102 		{
1103 			// Explicitly blocking everything
1104 			BlockedByLine(ld);
1105 			return false;
1106 		}
1107 
1108 		if ((EntityFlags & VEntity::EF_CheckLineBlockMonsters) &&
1109 			(ld->flags & ML_BLOCKMONSTERS))
1110 		{
1111 			// Block monsters only
1112 			BlockedByLine(ld);
1113 			return false;
1114 		}
1115 
1116 		if ((EntityFlags & VEntity::EF_IsPlayer) &&
1117 			(ld->flags & ML_BLOCKPLAYERS))
1118 		{
1119 			// Block players only
1120 			BlockedByLine(ld);
1121 			return false;
1122 		}
1123 
1124 		if ((EntityFlags & VEntity::EF_Float) &&
1125 			(ld->flags & ML_BLOCK_FLOATERS))
1126 		{
1127 			// Block floaters only
1128 			BlockedByLine(ld);
1129 			return false;
1130 		}
1131 	}
1132 
1133 	// set openrange, opentop, openbottom
1134 	TVec hit_point = tmtrace.End - (DotProduct(tmtrace.End, ld->normal) -
1135 		ld->dist) * ld->normal;
1136 	opening_t* open = SV_LineOpenings(ld, hit_point, SPF_NOBLOCKING);
1137 	open = SV_FindOpening(open, tmtrace.End.z, tmtrace.End.z + Height);
1138 
1139 	if (open)
1140 	{
1141 		// adjust floor / ceiling heights
1142 		if (!(open->ceiling->flags & SPF_NOBLOCKING) &&
1143 			open->top < tmtrace.CeilingZ)
1144 		{
1145 			tmtrace.Ceiling = open->ceiling;
1146 			tmtrace.CeilingZ = open->top;
1147 			tmtrace.CeilingLine = ld;
1148 		}
1149 
1150 		if (!(open->floor->flags & SPF_NOBLOCKING) &&
1151 			open->bottom > tmtrace.FloorZ)
1152 		{
1153 			tmtrace.Floor = open->floor;
1154 			tmtrace.FloorZ = open->bottom;
1155 		}
1156 
1157 		if (open->lowfloor < tmtrace.DropOffZ)
1158 		{
1159 			tmtrace.DropOffZ = open->lowfloor;
1160 		}
1161 
1162 		if (ld->flags & ML_RAILING)
1163 		{
1164 			tmtrace.FloorZ += 32;
1165 		}
1166 	}
1167 	else
1168 	{
1169 		tmtrace.CeilingZ = tmtrace.FloorZ;
1170 	}
1171 
1172 	// if contacted a special line, add it to the list
1173 	if (ld->special)
1174 	{
1175 		tmtrace.SpecHit.Append(ld);
1176 	}
1177 
1178 	return true;
1179 	unguardSlow;
1180 }
1181 
1182 //==========================================================================
1183 //
1184 //	VEntity::BlockedByLine
1185 //
1186 //==========================================================================
1187 
BlockedByLine(line_t * ld)1188 void VEntity::BlockedByLine(line_t* ld)
1189 {
1190 	guardSlow(VEntity::BlockedByLine);
1191 	if (EntityFlags & EF_Blasted)
1192 	{
1193 		eventBlastedHitLine();
1194 	}
1195 	if (ld->special)
1196 	{
1197 		eventCheckForPushSpecial(ld, 0);
1198 	}
1199 	unguardSlow;
1200 }
1201 
1202 //==========================================================================
1203 //
1204 //  VEntity::TryMove
1205 //
1206 //  Attempt to move to a new position, crossing special lines.
1207 //
1208 //==========================================================================
1209 
TryMove(tmtrace_t & tmtrace,TVec newPos,bool AllowDropOff)1210 bool VEntity::TryMove(tmtrace_t& tmtrace, TVec newPos, bool AllowDropOff)
1211 {
1212 	guard(VEntity::TryMove);
1213 	bool check;
1214 	TVec oldorg;
1215 	int side;
1216 	int oldside;
1217 	line_t *ld;
1218 	sector_t* OldSec = Sector;
1219 
1220 	check = CheckRelPosition(tmtrace, newPos);
1221 	tmtrace.TraceFlags &= ~tmtrace_t::TF_FloatOk;
1222 	if (!check)
1223 	{
1224 		VEntity *O = tmtrace.BlockingMobj;
1225 		if (!O || !(EntityFlags & EF_IsPlayer) ||
1226 			(O->EntityFlags & EF_IsPlayer) ||
1227 			O->Origin.z + O->Height - Origin.z > MaxStepHeight ||
1228 			O->CeilingZ - (O->Origin.z + O->Height) < Height ||
1229 			tmtrace.CeilingZ - (O->Origin.z + O->Height) < Height)
1230 		{
1231 			// Can't step up or doesn't fit
1232 			PushLine(tmtrace);
1233 			return false;
1234 		}
1235 		if (!(EntityFlags & EF_PassMobj) || compat_nopassover ||
1236 			(Level->LevelInfoFlags2 & VLevelInfo::LIF2_CompatNoPassOver))
1237 		{
1238 			// Can't go over
1239 			return false;
1240 		}
1241 	}
1242 
1243 	if (EntityFlags & EF_ColideWithWorld)
1244 	{
1245 		if (tmtrace.CeilingZ - tmtrace.FloorZ < Height)
1246 		{
1247 			// Doesn't fit
1248 			PushLine(tmtrace);
1249 			return false;
1250 		}
1251 
1252 		tmtrace.TraceFlags |= tmtrace_t::TF_FloatOk;
1253 
1254 		if (tmtrace.CeilingZ - Origin.z < Height && !(EntityFlags & EF_Fly) &&
1255 			!(EntityFlags & EF_IgnoreCeilingStep))
1256 		{
1257 			// mobj must lower itself to fit
1258 			PushLine(tmtrace);
1259 			return false;
1260 		}
1261 		if (EntityFlags & EF_Fly)
1262 		{
1263 			// When flying, slide up or down blocking lines until the actor
1264 			// is not blocked.
1265 			if (Origin.z + Height > tmtrace.CeilingZ)
1266 			{
1267 				// If sliding down, make sure we don't have another object below.
1268 				if ((!tmtrace.BlockingMobj || !tmtrace.BlockingMobj->CheckOnmobj() ||
1269 					(tmtrace.BlockingMobj->CheckOnmobj() &&
1270 					 tmtrace.BlockingMobj->CheckOnmobj() != this)) &&
1271 					(!CheckOnmobj() || (CheckOnmobj() &&
1272 					 CheckOnmobj() != tmtrace.BlockingMobj)))
1273 				{
1274 					Velocity.z = -8.0 * 35.0;
1275 				}
1276 				PushLine(tmtrace);
1277 				return false;
1278 			}
1279 			else if (Origin.z < tmtrace.FloorZ &&
1280 				tmtrace.FloorZ - tmtrace.DropOffZ > MaxStepHeight)
1281 			{
1282 				// Check to make sure there's nothing in the way for the step up
1283 				if ((!tmtrace.BlockingMobj || !tmtrace.BlockingMobj->CheckOnmobj() ||
1284 					(tmtrace.BlockingMobj->CheckOnmobj() &&
1285 					 tmtrace.BlockingMobj->CheckOnmobj() != this)) &&
1286 					(!CheckOnmobj() || (CheckOnmobj() &&
1287 					 CheckOnmobj() != tmtrace.BlockingMobj)))
1288 				{
1289 					Velocity.z = 8.0 * 35.0;
1290 				}
1291 				PushLine(tmtrace);
1292 				return false;
1293 			}
1294 		}
1295 		if (!(EntityFlags & EF_IgnoreFloorStep))
1296 		{
1297 			if (tmtrace.FloorZ - Origin.z > MaxStepHeight)
1298 			{
1299 				// Too big a step up
1300 				if (EntityFlags & EF_CanJump)
1301 				{
1302 					// Check to make sure there's nothing in the way for the step up
1303 					if ((tmtrace.BlockingMobj && (tmtrace.BlockingMobj->CheckOnmobj() &&
1304 						tmtrace.BlockingMobj->CheckOnmobj() == this)) ||
1305 						TestMobjZ(TVec(newPos.x, newPos.y, tmtrace.FloorZ)))
1306 					{
1307 						PushLine(tmtrace);
1308 						return false;
1309 					}
1310 				}
1311 				else
1312 				{
1313 					PushLine(tmtrace);
1314 					return false;
1315 				}
1316 			}
1317 			if ((EntityFlags & EF_Missile) && !(EntityFlags & EF_StepMissile) &&
1318 				tmtrace.FloorZ > Origin.z)
1319 			{
1320 				PushLine(tmtrace);
1321 				return false;
1322 			}
1323 			if (Origin.z < tmtrace.FloorZ)
1324 			{
1325 				// Check to make sure there's nothing in the way for the step up
1326 				if (TestMobjZ(TVec(newPos.x, newPos.y, tmtrace.FloorZ)))
1327 				{
1328 					PushLine(tmtrace);
1329 					return false;
1330 				}
1331 			}
1332 		}
1333 		// killough 3/15/98: Allow certain objects to drop off
1334 		if ((!AllowDropOff && !(EntityFlags & EF_DropOff) &&
1335 			!(EntityFlags & EF_Float) && !(EntityFlags & EF_Missile)) ||
1336 			(EntityFlags & EF_NoDropOff))
1337 		{
1338 			if (!(EntityFlags & EF_AvoidingDropoff))
1339 			{
1340 				float floorz = tmtrace.FloorZ;
1341 				// [RH] If the thing is standing on something, use its current z as the floorz.
1342 				// This is so that it does not walk off of things onto a drop off.
1343 				if (EntityFlags & EF_OnMobj)
1344 				{
1345 					floorz = MAX(Origin.z, tmtrace.FloorZ);
1346 				}
1347 
1348 				if ((floorz - tmtrace.DropOffZ > MaxDropoffHeight) &&
1349 					!(EntityFlags & EF_Blasted))
1350 				{
1351 					// Can't move over a dropoff unless it's been blasted
1352 					return false;
1353 				}
1354 			}
1355 			else
1356 			{
1357 				// special logic to move a monster off a dropoff
1358 				// this intentionally does not check for standing on things.
1359 				if (FloorZ - tmtrace.FloorZ > MaxDropoffHeight ||
1360 					DropOffZ - tmtrace.DropOffZ > MaxDropoffHeight)
1361 				{
1362 					return false;
1363 				}
1364 			}
1365 		}
1366 		if (EntityFlags & EF_CantLeaveFloorpic &&
1367 			(tmtrace.Floor->pic != Floor->pic || tmtrace.FloorZ != Origin.z))
1368 		{
1369 			// must stay within a sector of a certain floor type
1370 			return false;
1371 		}
1372 	}
1373 
1374 	bool OldAboveFakeFloor = false;
1375 	bool OldAboveFakeCeiling = false;
1376 	if (Sector->heightsec)
1377 	{
1378 		float EyeZ = Player ? Player->ViewOrg.z : Origin.z + Height * 0.5;
1379 		OldAboveFakeFloor = EyeZ > Sector->heightsec->floor.GetPointZ(Origin);
1380 		OldAboveFakeCeiling = EyeZ > Sector->heightsec->ceiling.GetPointZ(
1381 			Origin);
1382 	}
1383 
1384 	// the move is ok,
1385 	// so link the thing into its new position
1386 	UnlinkFromWorld();
1387 
1388 	oldorg = Origin;
1389 	Origin = newPos;
1390 
1391 	LinkToWorld();
1392 	FloorZ = tmtrace.FloorZ;
1393 	CeilingZ = tmtrace.CeilingZ;
1394 	DropOffZ = tmtrace.DropOffZ;
1395 	Floor = tmtrace.Floor;
1396 	Ceiling = tmtrace.Ceiling;
1397 
1398 	if (EntityFlags & EF_FloorClip)
1399 	{
1400 		eventHandleFloorclip();
1401 	}
1402 	else
1403 	{
1404 		FloorClip = 0.0;
1405 	}
1406 
1407 	//
1408 	// if any special lines were hit, do the effect
1409 	//
1410 	if (EntityFlags & EF_ColideWithWorld)
1411 	{
1412 		while (tmtrace.SpecHit.Num() > 0)
1413 		{
1414 			// see if the line was crossed
1415 			ld = tmtrace.SpecHit[tmtrace.SpecHit.Num() - 1];
1416 			tmtrace.SpecHit.SetNum(tmtrace.SpecHit.Num() - 1, false);
1417 			side = ld->PointOnSide(Origin);
1418 			oldside = ld->PointOnSide(oldorg);
1419 			if (side != oldside)
1420 			{
1421 				if (ld->special)
1422 				{
1423 					eventCrossSpecialLine(ld, oldside);
1424 				}
1425 			}
1426 		}
1427 	}
1428 
1429 	//	Do additional check here to avoid calling progs.
1430 	if ((OldSec->heightsec && Sector->heightsec && Sector->ActionList) ||
1431 		(OldSec != Sector && (OldSec->ActionList || Sector->ActionList)))
1432 	{
1433 		eventCheckForSectorActions(OldSec, OldAboveFakeFloor,
1434 			OldAboveFakeCeiling);
1435 	}
1436 
1437 	return true;
1438 	unguard;
1439 }
1440 
1441 //==========================================================================
1442 //
1443 //  VEntity::PushLine
1444 //
1445 //==========================================================================
1446 
PushLine(const tmtrace_t & tmtrace)1447 void VEntity::PushLine(const tmtrace_t& tmtrace)
1448 {
1449 	guardSlow(VEntity::PushLine);
1450 	if (EntityFlags & EF_ColideWithWorld)
1451 	{
1452 		if (EntityFlags & EF_Blasted)
1453 		{
1454 			eventBlastedHitLine();
1455 		}
1456 		int NumSpecHitTemp = tmtrace.SpecHit.Num();
1457 		while (NumSpecHitTemp > 0)
1458 		{
1459 			NumSpecHitTemp--;
1460 			// see if the line was crossed
1461 			line_t* ld = tmtrace.SpecHit[NumSpecHitTemp];
1462 			int side = ld->PointOnSide(Origin);
1463 			eventCheckForPushSpecial(ld, side);
1464 		}
1465 	}
1466 	unguardSlow;
1467 }
1468 
1469 //**************************************************************************
1470 //
1471 //  SLIDE MOVE
1472 //
1473 //  Allows the player to slide along any angled walls.
1474 //
1475 //**************************************************************************
1476 
1477 //==========================================================================
1478 //
1479 //  VEntity::ClipVelocity
1480 //
1481 //  Slide off of the impacting object
1482 //
1483 //==========================================================================
1484 
ClipVelocity(const TVec & in,const TVec & normal,float overbounce)1485 TVec VEntity::ClipVelocity(const TVec& in, const TVec& normal, float overbounce)
1486 {
1487 	return in - normal * (DotProduct(in, normal) * overbounce);
1488 }
1489 
1490 //==========================================================================
1491 //
1492 //  VEntity::SlidePathTraverse
1493 //
1494 //==========================================================================
1495 
SlidePathTraverse(float & BestSlideFrac,line_t * & BestSlideLine,float x,float y,float StepVelScale)1496 void VEntity::SlidePathTraverse(float& BestSlideFrac, line_t*& BestSlideLine,
1497 	float x, float y, float StepVelScale)
1498 {
1499 	guard(VEntity::SlidePathTraverse);
1500 	TVec SlideOrg(x, y, Origin.z);
1501 	TVec SlideDir = Velocity * StepVelScale;
1502 	intercept_t*	in;
1503 	for (VPathTraverse It(this, &in, x, y, x + SlideDir.x,
1504 		y + SlideDir.y, PT_ADDLINES); It.GetNext(); )
1505 	{
1506 		if (!(in->Flags & intercept_t::IF_IsALine))
1507 		{
1508 			Host_Error("PTR_SlideTraverse: not a line?");
1509 		}
1510 
1511 		line_t* li = in->line;
1512 
1513 		bool IsBlocked = false;
1514 		if (!(li->flags & ML_TWOSIDED) || !li->backsector)
1515 		{
1516 			if (li->PointOnSide(Origin))
1517 			{
1518 				// don't hit the back side
1519 				continue;
1520 			}
1521 			IsBlocked = true;
1522 		}
1523 		else if (li->flags & (ML_BLOCKING | ML_BLOCKEVERYTHING))
1524 		{
1525 			IsBlocked = true;
1526 		}
1527 		else if ((EntityFlags & EF_IsPlayer) && (li->flags & ML_BLOCKPLAYERS))
1528 		{
1529 			IsBlocked = true;
1530 		}
1531 		else if ((EntityFlags & EF_CheckLineBlockMonsters) && (li->flags & ML_BLOCKMONSTERS))
1532 		{
1533 			IsBlocked = true;
1534 		}
1535 
1536 		if (!IsBlocked)
1537 		{
1538 			// set openrange, opentop, openbottom
1539 			TVec hit_point = SlideOrg + in->frac * SlideDir;
1540 			opening_t* open = SV_LineOpenings(li, hit_point, SPF_NOBLOCKING);
1541 			open = SV_FindOpening(open, Origin.z, Origin.z + Height);
1542 
1543 			if (open && (open->range >= Height) &&	//  fits
1544 				(open->top - Origin.z >= Height) &&	// mobj is not too high
1545 				(open->bottom - Origin.z <= MaxStepHeight))	// not too big a step up
1546 			{
1547 				// this line doesn't block movement
1548 				if (Origin.z < open->bottom)
1549 				{
1550 					// Check to make sure there's nothing in the way for the step up
1551 					TVec CheckOrg = Origin;
1552 					CheckOrg.z = open->bottom;
1553 					if (!TestMobjZ(CheckOrg))
1554 					{
1555 						continue;
1556 					}
1557 				}
1558 				else
1559 				{
1560 					continue;
1561 				}
1562 			}
1563 		}
1564 
1565 		// the line blocks movement,
1566 		// see if it is closer than best so far
1567 		if (in->frac < BestSlideFrac)
1568 		{
1569 			BestSlideFrac = in->frac;
1570 			BestSlideLine = li;
1571 		}
1572 
1573 		break;	// stop
1574 	}
1575 	unguard;
1576 }
1577 
1578 //==========================================================================
1579 //
1580 //  VEntity::SlideMove
1581 //
1582 //  The momx / momy move is bad, so try to slide along a wall.
1583 //  Find the first line hit, move flush to it, and slide along it.
1584 //  This is a kludgy mess.
1585 //
1586 //==========================================================================
1587 
SlideMove(float StepVelScale)1588 void VEntity::SlideMove(float StepVelScale)
1589 {
1590 	guard(VEntity::SlideMove);
1591 	float leadx;
1592 	float leady;
1593 	float trailx;
1594 	float traily;
1595 	float newx;
1596 	float newy;
1597 	int hitcount;
1598 	tmtrace_t tmtrace;
1599 
1600 	hitcount = 0;
1601 
1602 	float XMove = Velocity.x * StepVelScale;
1603 	float YMove = Velocity.y * StepVelScale;
1604 	do
1605 	{
1606 		if (++hitcount == 3)
1607 		{
1608 			// don't loop forever
1609 			if (!TryMove(tmtrace, TVec(Origin.x, Origin.y + YMove, Origin.z),
1610 				true))
1611 			{
1612 				TryMove(tmtrace, TVec(Origin.x + XMove, Origin.y, Origin.z),
1613 					true);
1614 			}
1615 			return;
1616 		}
1617 
1618 		// trace along the three leading corners
1619 		if (XMove > 0.0)
1620 		{
1621 			leadx = Origin.x + Radius;
1622 			trailx = Origin.x - Radius;
1623 		}
1624 		else
1625 		{
1626 			leadx = Origin.x - Radius;
1627 			trailx = Origin.x + Radius;
1628 		}
1629 
1630 		if (Velocity.y > 0.0)
1631 		{
1632 			leady = Origin.y + Radius;
1633 			traily = Origin.y - Radius;
1634 		}
1635 		else
1636 		{
1637 			leady = Origin.y - Radius;
1638 			traily = Origin.y + Radius;
1639 		}
1640 
1641 		float BestSlideFrac = 1.00001f;
1642 		line_t* BestSlideLine = NULL;
1643 
1644 		SlidePathTraverse(BestSlideFrac, BestSlideLine, leadx, leady, StepVelScale);
1645 		SlidePathTraverse(BestSlideFrac, BestSlideLine, trailx, leady, StepVelScale);
1646 		SlidePathTraverse(BestSlideFrac, BestSlideLine, leadx, traily, StepVelScale);
1647 
1648 		// move up to the wall
1649 		if (BestSlideFrac == 1.00001f)
1650 		{
1651 			// the move must have hit the middle, so stairstep
1652 			if (!TryMove(tmtrace, TVec(Origin.x, Origin.y + YMove, Origin.z),
1653 				true))
1654 			{
1655 				TryMove(tmtrace, TVec(Origin.x + XMove, Origin.y, Origin.z),
1656 					true);
1657 			}
1658 			return;
1659 		}
1660 
1661 		// fudge a bit to make sure it doesn't hit
1662 		BestSlideFrac -= 0.03125;
1663 		if (BestSlideFrac > 0.0)
1664 		{
1665 			newx = XMove * BestSlideFrac;
1666 			newy = YMove * BestSlideFrac;
1667 
1668 			if (!TryMove(tmtrace, TVec(Origin.x + newx, Origin.y + newy,
1669 				Origin.z), true))
1670 			{
1671 				if (!TryMove(tmtrace, TVec(Origin.x, Origin.y + YMove,
1672 					Origin.z), true))
1673 				{
1674 					TryMove(tmtrace, TVec(Origin.x + XMove, Origin.y,
1675 						Origin.z), true);
1676 				}
1677 				return;
1678 			}
1679 		}
1680 
1681 		// Now continue along the wall.
1682 		// First calculate remainder.
1683 		BestSlideFrac = 1.0 - (BestSlideFrac + 0.03125);
1684 
1685 		if (BestSlideFrac > 1.0)
1686 		{
1687 			BestSlideFrac = 1.0;
1688 		}
1689 
1690 		if (BestSlideFrac <= 0.0)
1691 		{
1692 			return;
1693 		}
1694 
1695 		// clip the moves
1696 		Velocity = ClipVelocity(Velocity * BestSlideFrac,
1697 			BestSlideLine->normal, 1.0);
1698 		XMove = Velocity.x * StepVelScale;
1699 		YMove = Velocity.y * StepVelScale;
1700 	}
1701 	while (!TryMove(tmtrace, TVec(Origin.x + XMove, Origin.y + YMove,
1702 		Origin.z), true));
1703 	unguard;
1704 }
1705 
1706 //**************************************************************************
1707 //
1708 //  BOUNCING
1709 //
1710 //  Bounce missile against walls
1711 //
1712 //**************************************************************************
1713 
1714 //============================================================================
1715 //
1716 //  VEntity::BounceWall
1717 //
1718 //============================================================================
1719 
BounceWall(float overbounce,float bouncefactor)1720 void VEntity::BounceWall(float overbounce, float bouncefactor)
1721 {
1722 	guard(VEntity::BounceWall);
1723 	TVec SlideOrg;
1724 	if (Velocity.x > 0.0)
1725 	{
1726 		SlideOrg.x = Origin.x + Radius;
1727 	}
1728 	else
1729 	{
1730 		SlideOrg.x = Origin.x - Radius;
1731 	}
1732 	if (Velocity.y > 0.0)
1733 	{
1734 		SlideOrg.y = Origin.y + Radius;
1735 	}
1736 	else
1737 	{
1738 		SlideOrg.y = Origin.y - Radius;
1739 	}
1740 	SlideOrg.z = Origin.z;
1741 	TVec SlideDir = Velocity * host_frametime;
1742 	line_t* BestSlideLine = NULL;
1743 	intercept_t* in;
1744 	for (VPathTraverse It(this, &in, SlideOrg.x, SlideOrg.y, SlideOrg.x +
1745 		SlideDir.x, SlideOrg.y + SlideDir.y, PT_ADDLINES); It.GetNext(); )
1746 	{
1747 		if (!(in->Flags & intercept_t::IF_IsALine))
1748 		{
1749 			Host_Error("PTR_BounceTraverse: not a line?");
1750 		}
1751 
1752 		line_t* li = in->line;
1753 		if (li->flags & ML_TWOSIDED)
1754 		{
1755 			TVec hit_point = SlideOrg + in->frac * SlideDir;
1756 			// set openrange, opentop, openbottom
1757 			opening_t* open = SV_LineOpenings(li, hit_point, SPF_NOBLOCKING);
1758 			open = SV_FindOpening(open, Origin.z, Origin.z + Height);
1759 			if (open && open->range >= Height &&	// fits
1760 				Origin.z + Height <= open->top &&
1761 				Origin.z >= open->bottom)	// mobj is not too high
1762 			{
1763 				continue;	// this line doesn't block movement
1764 			}
1765 		}
1766 		else
1767 		{
1768 			if (li->PointOnSide(Origin))
1769 			{
1770 				continue;	// don't hit the back side
1771 			}
1772 		}
1773 
1774 		BestSlideLine = li;
1775 		break;	// don't bother going farther
1776 	}
1777 	if (BestSlideLine)
1778 	{
1779 //		Velocity.x *= bouncefactor;
1780 //		Velocity.y *= bouncefactor;
1781 //		Velocity.z *= bouncefactor;
1782 		Velocity = ClipVelocity(Velocity * bouncefactor, BestSlideLine->normal, overbounce * bouncefactor);
1783 	}
1784 	unguard;
1785 }
1786 
1787 //==========================================================================
1788 //
1789 //  VEntity::UpdateVelocity
1790 //
1791 //==========================================================================
1792 
UpdateVelocity()1793 void VEntity::UpdateVelocity()
1794 {
1795 	guard(VEntity::UpdateVelocity);
1796 	float startvelz, sinkspeed;
1797 
1798 /*	if (Origin.z <= FloorZ && !Velocity.x && !Velocity.y &&
1799 		!Velocity.z && !bCountKill && !(EntityFlags & EF_IsPlayer))
1800 	{
1801 		//  No gravity for non-moving things on ground to prevent
1802 		// static objects from sliding on slopes
1803 		return;
1804 	}*/
1805 
1806 	//  Don't add gravity if standing on slope with normal.z > 0.7 (aprox
1807 	// 45 degrees)
1808 	if (!(EntityFlags & EF_NoGravity) && (Origin.z > FloorZ ||
1809 		Floor->normal.z <= 0.7))
1810 	{
1811 		if (WaterLevel < 2)
1812 		{
1813 			Velocity.z -= Gravity * Level->Gravity * Sector->Gravity *
1814 				host_frametime;
1815 		}
1816 		else if (!(EntityFlags & EF_IsPlayer) || Health <= 0)
1817 		{
1818 			// Water Gravity
1819 			Velocity.z -= Gravity * Level->Gravity * Sector->Gravity / 10.0 *
1820 				host_frametime;
1821 			startvelz = Velocity.z;
1822 
1823 			if (EntityFlags & EF_Corpse)
1824 				sinkspeed = -WATER_SINK_SPEED / 3.0;
1825 			else
1826 				sinkspeed = -WATER_SINK_SPEED;
1827 
1828 			if (Velocity.z < sinkspeed)
1829 			{
1830 				if (startvelz < sinkspeed)
1831 					Velocity.z = startvelz;
1832 				else
1833 					Velocity.z = sinkspeed;
1834 			}
1835 			else
1836 			{
1837 				Velocity.z = startvelz + (Velocity.z - startvelz) *
1838 					WATER_SINK_FACTOR;
1839 			}
1840 		}
1841 	}
1842 
1843 	// Friction
1844 	if (Velocity.x || Velocity.y/* || Velocity.z*/)
1845 	{
1846 		eventApplyFriction();
1847 	}
1848 	unguard;
1849 }
1850 
1851 //**************************************************************************
1852 //
1853 //  TEST ON MOBJ
1854 //
1855 //**************************************************************************
1856 
1857 //=============================================================================
1858 //
1859 // TestMobjZ
1860 //
1861 //  Checks if the new Z position is legal
1862 //
1863 //=============================================================================
1864 
TestMobjZ(const TVec & TryOrg)1865 VEntity* VEntity::TestMobjZ(const TVec& TryOrg)
1866 {
1867 	guard(VEntity::TestMobjZ);
1868 	int xl, xh, yl, yh, bx, by;
1869 
1870 	// Can't hit thing
1871 	if (!(EntityFlags & EF_ColideWithThings))
1872 	{
1873 		return NULL;
1874 	}
1875 
1876 	//
1877 	// the bounding box is extended by MAXRADIUS because mobj_ts are grouped
1878 	// into mapblocks based on their origin point, and can overlap into adjacent
1879 	// blocks by up to MAXRADIUS units
1880 	//
1881 	xl = MapBlock(TryOrg.x - Radius - XLevel->BlockMapOrgX - MAXRADIUS);
1882 	xh = MapBlock(TryOrg.x + Radius - XLevel->BlockMapOrgX + MAXRADIUS);
1883 	yl = MapBlock(TryOrg.y - Radius - XLevel->BlockMapOrgY - MAXRADIUS);
1884 	yh = MapBlock(TryOrg.y + Radius - XLevel->BlockMapOrgY + MAXRADIUS);
1885 
1886 	// xl->xh, yl->yh determine the mapblock set to search
1887 	for (bx = xl; bx <= xh; bx++)
1888 	{
1889 		for (by = yl; by <= yh; by++)
1890 		{
1891 			for (VBlockThingsIterator Other(XLevel, bx, by); Other; ++Other)
1892 			{
1893 				if (!(Other->EntityFlags & EF_Solid))
1894 				{
1895 					// Can't hit thing
1896 					continue;
1897 				}
1898 				if (*Other == this)
1899 				{
1900 					// Don't clip against self
1901 					continue;
1902 				}
1903 				if (TryOrg.z > Other->Origin.z + Other->Height)
1904 				{
1905 					// over thing
1906 					continue;
1907 				}
1908 				if (TryOrg.z + Height < Other->Origin.z)
1909 				{
1910 					// under thing
1911 					continue;
1912 				}
1913 				float blockdist = Other->Radius + Radius;
1914 				if (fabs(Other->Origin.x - TryOrg.x) >= blockdist ||
1915 					fabs(Other->Origin.y - TryOrg.y) >= blockdist)
1916 				{
1917 					// Didn't hit thing
1918 					continue;
1919 				}
1920 				return *Other;
1921 			}
1922 		}
1923 	}
1924 
1925 	return NULL;
1926 	unguard;
1927 }
1928 
1929 //=============================================================================
1930 //
1931 //  VEntity::FakeZMovement
1932 //
1933 //  Fake the zmovement so that we can check if a move is legal
1934 //
1935 //=============================================================================
1936 
FakeZMovement()1937 TVec VEntity::FakeZMovement()
1938 {
1939 	guard(VEntity::FakeZMovement);
1940 	TVec Ret;
1941 	eventCalcFakeZMovement(Ret, host_frametime);
1942 
1943 	//
1944 	//  clip movement
1945 	//
1946 	if (Ret.z <= FloorZ)
1947 	{
1948 		// Hit the floor
1949 		Ret.z = FloorZ;
1950 	}
1951 	if (Ret.z + Height > CeilingZ)
1952 	{
1953 		// hit the ceiling
1954 		Ret.z = CeilingZ - Height;
1955 	}
1956 	return Ret;
1957 	unguard;
1958 }
1959 
1960 //=============================================================================
1961 //
1962 //  VEntity::CheckOnmobj
1963 //
1964 //  Checks if an object is above another object
1965 //
1966 //=============================================================================
1967 
CheckOnmobj()1968 VEntity* VEntity::CheckOnmobj()
1969 {
1970 	guard(VEntity::CheckOnmobj);
1971 	return TestMobjZ(FakeZMovement());
1972 	unguard;
1973 }
1974 
1975 //==========================================================================
1976 //
1977 //  VEntity::CheckSides
1978 //
1979 // This routine checks for Lost Souls trying to be spawned		// phares
1980 // across 1-sided lines, impassible lines, or "monsters can't	//   |
1981 // cross" lines. Draw an imaginary line between the PE			//   V
1982 // and the new Lost Soul spawn spot. If that line crosses
1983 // a 'blocking' line, then disallow the spawn. Only search
1984 // lines in the blocks of the blockmap where the bounding box
1985 // of the trajectory line resides. Then check bounding box
1986 // of the trajectory vs. the bounding box of each blocking
1987 // line to see if the trajectory and the blocking line cross.
1988 // Then check the PE and LS to see if they're on different
1989 // sides of the blocking line. If so, return true, otherwise
1990 // false.
1991 //
1992 //==========================================================================
1993 
CheckSides(TVec lsPos)1994 bool VEntity::CheckSides(TVec lsPos)
1995 {
1996 	guard(VEntity::CheckSides);
1997 	int bx,by,xl,xh,yl,yh;
1998 
1999 	// Here is the bounding box of the trajectory
2000 	float tmbbox[4];
2001 	tmbbox[BOXLEFT] = MIN(Origin.x, lsPos.x);
2002 	tmbbox[BOXRIGHT] = MAX(Origin.x, lsPos.x);
2003 	tmbbox[BOXTOP] = MAX(Origin.y, lsPos.y);
2004 	tmbbox[BOXBOTTOM] = MIN(Origin.y, lsPos.y);
2005 
2006 	// Determine which blocks to look in for blocking lines
2007 	xl = MapBlock(tmbbox[BOXLEFT] - XLevel->BlockMapOrgX);
2008 	xh = MapBlock(tmbbox[BOXRIGHT] - XLevel->BlockMapOrgX);
2009 	yl = MapBlock(tmbbox[BOXBOTTOM] - XLevel->BlockMapOrgY);
2010 	yh = MapBlock(tmbbox[BOXTOP] - XLevel->BlockMapOrgY);
2011 
2012 	// xl->xh, yl->yh determine the mapblock set to search
2013 	validcount++; // prevents checking same line twice
2014 	for (bx = xl; bx <= xh; bx++)
2015 	{
2016 		for (by = yl; by <= yh; by++)
2017 		{
2018 			line_t* ld;
2019 			for (VBlockLinesIterator It(XLevel, bx, by, &ld); It.GetNext(); )
2020 			{
2021 				// Checks to see if a PE->LS trajectory line crosses a blocking
2022 				// line. Returns false if it does.
2023 				//
2024 				// tmbbox holds the bounding box of the trajectory. If that box
2025 				// does not touch the bounding box of the line in question,
2026 				// then the trajectory is not blocked. If the PE is on one side
2027 				// of the line and the LS is on the other side, then the
2028 				// trajectory is blocked.
2029 				//
2030 				// Currently this assumes an infinite line, which is not quite
2031 				// correct. A more correct solution would be to check for an
2032 				// intersection of the trajectory and the line, but that takes
2033 				// longer and probably really isn't worth the effort.
2034 
2035 				if (ld->flags & (ML_BLOCKING | ML_BLOCKMONSTERS |
2036 					ML_BLOCKEVERYTHING))
2037 				{
2038 					if (tmbbox[BOXLEFT] <= ld->bbox[BOXRIGHT] &&
2039 						tmbbox[BOXRIGHT] >= ld->bbox[BOXLEFT] &&
2040 						tmbbox[BOXTOP] >= ld->bbox[BOXBOTTOM] &&
2041 						tmbbox[BOXBOTTOM] <= ld->bbox[BOXTOP])
2042 					{
2043 						if (ld->PointOnSide(Origin) != ld->PointOnSide(lsPos))
2044 						{
2045 								return true;  // line blocks trajectory
2046 						}
2047 					}
2048 				}
2049 
2050 				// line doesn't block trajectory
2051 			}
2052 		}
2053 	}
2054 
2055 	return false;
2056 	unguard;
2057 }
2058 
2059 //=============================================================================
2060 //
2061 //	CheckDropOff
2062 //
2063 //	killough 11/98:
2064 //
2065 //	Monsters try to move away from tall dropoffs.
2066 //
2067 //	In Doom, they were never allowed to hang over dropoffs, and would remain
2068 // stuck if involuntarily forced over one. This logic, combined with P_TryMove,
2069 // allows monsters to free themselves without making them tend to hang over
2070 // dropoffs.
2071 //
2072 //=============================================================================
2073 
CheckDropOff(float & DeltaX,float & DeltaY)2074 void VEntity::CheckDropOff(float& DeltaX, float& DeltaY)
2075 {
2076 	guard(VEntity::CheckDropOff);
2077 	float t_bbox[4];
2078 	int xl;
2079 	int xh;
2080 	int yl;
2081 	int yh;
2082 	int bx;
2083 	int by;
2084 
2085 	// Try to move away from a dropoff
2086 	DeltaX = 0;
2087 	DeltaY = 0;
2088 
2089 	t_bbox[BOXTOP]   = Origin.y + Radius;
2090 	t_bbox[BOXBOTTOM]= Origin.y - Radius;
2091 	t_bbox[BOXRIGHT] = Origin.x + Radius;
2092 	t_bbox[BOXLEFT]  = Origin.x - Radius;
2093 
2094 	xl = MapBlock(t_bbox[BOXLEFT] - XLevel->BlockMapOrgX);
2095 	xh = MapBlock(t_bbox[BOXRIGHT] - XLevel->BlockMapOrgX);
2096 	yl = MapBlock(t_bbox[BOXBOTTOM] - XLevel->BlockMapOrgY);
2097 	yh = MapBlock(t_bbox[BOXTOP] - XLevel->BlockMapOrgY);
2098 
2099 	// check lines
2100 	validcount++;
2101 	for (bx = xl; bx <= xh; bx++)
2102 	{
2103 		for (by = yl; by <= yh; by++)
2104 		{
2105 			line_t* line;
2106 			for (VBlockLinesIterator It(XLevel, bx, by, &line); It.GetNext(); )
2107 			{
2108 				// Ignore one-sided linedefs
2109 				if (!line->backsector)
2110 				{
2111 					continue;
2112 				}
2113 				// Linedef must be contacted
2114 				if (t_bbox[BOXRIGHT] > line->bbox[BOXLEFT] &&
2115 					t_bbox[BOXLEFT] < line->bbox[BOXRIGHT] &&
2116 					t_bbox[BOXTOP] > line->bbox[BOXBOTTOM] &&
2117 					t_bbox[BOXBOTTOM] < line->bbox[BOXTOP] &&
2118 					P_BoxOnLineSide(t_bbox, line) == -1)
2119 				{
2120 					// New logic for 3D Floors
2121 					sec_region_t*FrontReg = SV_FindThingGap(
2122 						line->frontsector->botregion,
2123 						Origin, Origin.z, Origin.z + Height);
2124 					sec_region_t*BackReg = SV_FindThingGap(
2125 						line->backsector->botregion,
2126 						Origin, Origin.z, Origin.z + Height);
2127 					float front = FrontReg->floor->GetPointZ(Origin);
2128 					float back = BackReg->floor->GetPointZ(Origin);
2129 
2130 					// The monster must contact one of the two floors,
2131 					// and the other must be a tall dropoff.
2132 					TVec Dir;
2133 					if (back == Origin.z &&
2134 						front < Origin.z - MaxDropoffHeight)
2135 					{
2136 						// front side dropoff
2137 						Dir = -line->normal;
2138 					}
2139 					else if (front == Origin.z &&
2140 						back < Origin.z - MaxDropoffHeight)
2141 					{
2142 						// back side dropoff
2143 						Dir = line->normal;
2144 					}
2145 					else
2146 					{
2147 						continue;
2148 					}
2149 					// Move away from dropoff at a standard speed.
2150 					// Multiple contacted linedefs are cumulative
2151 					// (e.g. hanging over corner)
2152 					DeltaX += Dir.x * 32.0;
2153 					DeltaY += Dir.y * 32.0;
2154 				}
2155 			}
2156 		}
2157 	}
2158 	unguard;
2159 }
2160 
2161 //==========================================================================
2162 //
2163 //	VRoughBlockSearchIterator
2164 //
2165 //==========================================================================
2166 
VRoughBlockSearchIterator(VEntity * ASelf,int ADistance,VEntity ** AEntPtr)2167 VRoughBlockSearchIterator::VRoughBlockSearchIterator(VEntity* ASelf,
2168 	int ADistance, VEntity** AEntPtr)
2169 : Self(ASelf)
2170 , Distance(ADistance)
2171 , Ent(NULL)
2172 , EntPtr(AEntPtr)
2173 , Count(1)
2174 , CurrentEdge(-1)
2175 {
2176 	guard(VRoughBlockSearchIterator::VRoughBlockSearchIterator);
2177 	StartX = MapBlock(Self->Origin.x - Self->XLevel->BlockMapOrgX);
2178 	StartY = MapBlock(Self->Origin.y - Self->XLevel->BlockMapOrgY);
2179 
2180 	//	Start with current block
2181 	if (StartX >= 0 && StartX < Self->XLevel->BlockMapWidth &&
2182 		StartY >= 0 && StartY < Self->XLevel->BlockMapHeight)
2183 	{
2184 		Ent = Self->XLevel->BlockLinks[StartY * Self->XLevel->BlockMapWidth +
2185 			StartX];
2186 	}
2187 	unguard;
2188 }
2189 
2190 //==========================================================================
2191 //
2192 //	VRoughBlockSearchIterator::GetNext
2193 //
2194 //==========================================================================
2195 
GetNext()2196 bool VRoughBlockSearchIterator::GetNext()
2197 {
2198 	guard(VRoughBlockSearchIterator::GetNext);
2199 	int		BlockX;
2200 	int		BlockY;
2201 
2202 	while (1)
2203 	{
2204 		if (Ent)
2205 		{
2206 			*EntPtr = Ent;
2207 			Ent = Ent->BlockMapNext;
2208 			return true;
2209 		}
2210 
2211 		switch (CurrentEdge)
2212 		{
2213 		case 0:
2214 			// Trace the first block section (along the top)
2215 			if (BlockIndex <= FirstStop)
2216 			{
2217 				Ent = Self->XLevel->BlockLinks[BlockIndex];
2218 				BlockIndex++;
2219 			}
2220 			else
2221 			{
2222 				CurrentEdge = 1;
2223 				BlockIndex--;
2224 			}
2225 			break;
2226 
2227 		case 1:
2228 			// Trace the second block section (right edge)
2229 			if (BlockIndex <= SecondStop)
2230 			{
2231 				Ent = Self->XLevel->BlockLinks[BlockIndex];
2232 				BlockIndex += Self->XLevel->BlockMapWidth;
2233 			}
2234 			else
2235 			{
2236 				CurrentEdge = 2;
2237 				BlockIndex -= Self->XLevel->BlockMapWidth;
2238 			}
2239 			break;
2240 
2241 		case 2:
2242 			// Trace the third block section (bottom edge)
2243 			if (BlockIndex >= ThirdStop)
2244 			{
2245 				Ent = Self->XLevel->BlockLinks[BlockIndex];
2246 				BlockIndex--;
2247 			}
2248 			else
2249 			{
2250 				CurrentEdge = 3;
2251 				BlockIndex++;
2252 			}
2253 			break;
2254 
2255 		case 3:
2256 			// Trace the final block section (left edge)
2257 			if (BlockIndex > FinalStop)
2258 			{
2259 				Ent = Self->XLevel->BlockLinks[BlockIndex];
2260 				BlockIndex -= Self->XLevel->BlockMapWidth;
2261 			}
2262 			else
2263 			{
2264 				CurrentEdge = -1;
2265 			}
2266 			break;
2267 
2268 		default:
2269 			if (Count > Distance)
2270 			{
2271 				//	We are done
2272 				return false;
2273 			}
2274 			BlockX = StartX - Count;
2275 			BlockY = StartY - Count;
2276 
2277 			if (BlockY < 0)
2278 			{
2279 				BlockY = 0;
2280 			}
2281 			else if (BlockY >= Self->XLevel->BlockMapHeight)
2282 			{
2283 				BlockY = Self->XLevel->BlockMapHeight - 1;
2284 			}
2285 			if (BlockX < 0)
2286 			{
2287 				BlockX = 0;
2288 			}
2289 			else if (BlockX >= Self->XLevel->BlockMapWidth)
2290 			{
2291 				BlockX = Self->XLevel->BlockMapWidth - 1;
2292 			}
2293 			BlockIndex = BlockY * Self->XLevel->BlockMapWidth + BlockX;
2294 			FirstStop = StartX + Count;
2295 			if (FirstStop < 0)
2296 			{
2297 				Count++;
2298 				break;
2299 			}
2300 			if (FirstStop >= Self->XLevel->BlockMapWidth)
2301 			{
2302 				FirstStop = Self->XLevel->BlockMapWidth - 1;
2303 			}
2304 			SecondStop = StartY + Count;
2305 			if (SecondStop < 0)
2306 			{
2307 				Count++;
2308 				break;
2309 			}
2310 			if (SecondStop >= Self->XLevel->BlockMapHeight)
2311 			{
2312 				SecondStop = Self->XLevel->BlockMapHeight - 1;
2313 			}
2314 			ThirdStop = SecondStop * Self->XLevel->BlockMapWidth + BlockX;
2315 			SecondStop = SecondStop * Self->XLevel->BlockMapWidth + FirstStop;
2316 			FirstStop += BlockY * Self->XLevel->BlockMapWidth;
2317 			FinalStop = BlockIndex;
2318 			Count++;
2319 			CurrentEdge = 0;
2320 			break;
2321 		}
2322 	}
2323 	return false;
2324 	unguard;
2325 }
2326 
2327 //==========================================================================
2328 //
2329 //	Script natives
2330 //
2331 //==========================================================================
2332 
IMPLEMENT_FUNCTION(VEntity,CheckWater)2333 IMPLEMENT_FUNCTION(VEntity, CheckWater)
2334 {
2335 	P_GET_SELF;
2336 	RET_INT(Self->CheckWater());
2337 }
2338 
IMPLEMENT_FUNCTION(VEntity,CheckDropOff)2339 IMPLEMENT_FUNCTION(VEntity, CheckDropOff)
2340 {
2341 	P_GET_PTR(float, DeltaX);
2342 	P_GET_PTR(float, DeltaY);
2343 	P_GET_SELF;
2344 	Self->CheckDropOff(*DeltaX, *DeltaY);
2345 }
2346 
IMPLEMENT_FUNCTION(VEntity,CheckPosition)2347 IMPLEMENT_FUNCTION(VEntity, CheckPosition)
2348 {
2349 	P_GET_VEC(Pos);
2350 	P_GET_SELF;
2351 	RET_BOOL(Self->CheckPosition(Pos));
2352 }
2353 
IMPLEMENT_FUNCTION(VEntity,CheckRelPosition)2354 IMPLEMENT_FUNCTION(VEntity, CheckRelPosition)
2355 {
2356 	P_GET_VEC(Pos);
2357 	P_GET_PTR(tmtrace_t, tmtrace);
2358 	P_GET_SELF;
2359 	RET_BOOL(Self->CheckRelPosition(*tmtrace, Pos));
2360 }
2361 
IMPLEMENT_FUNCTION(VEntity,CheckSides)2362 IMPLEMENT_FUNCTION(VEntity, CheckSides)
2363 {
2364 	P_GET_VEC(lsPos);
2365 	P_GET_SELF;
2366 	RET_BOOL(Self->CheckSides(lsPos));
2367 }
2368 
IMPLEMENT_FUNCTION(VEntity,TryMove)2369 IMPLEMENT_FUNCTION(VEntity, TryMove)
2370 {
2371 	P_GET_BOOL(AllowDropOff);
2372 	P_GET_VEC(Pos);
2373 	P_GET_SELF;
2374 	tmtrace_t tmtrace;
2375 	RET_BOOL(Self->TryMove(tmtrace, Pos, AllowDropOff));
2376 }
2377 
IMPLEMENT_FUNCTION(VEntity,TryMoveEx)2378 IMPLEMENT_FUNCTION(VEntity, TryMoveEx)
2379 {
2380 	P_GET_BOOL(AllowDropOff);
2381 	P_GET_VEC(Pos);
2382 	P_GET_PTR(tmtrace_t, tmtrace);
2383 	P_GET_SELF;
2384 	RET_BOOL(Self->TryMove(*tmtrace, Pos, AllowDropOff));
2385 }
2386 
IMPLEMENT_FUNCTION(VEntity,TestMobjZ)2387 IMPLEMENT_FUNCTION(VEntity, TestMobjZ)
2388 {
2389 	P_GET_SELF;
2390 	RET_BOOL(!Self->TestMobjZ(Self->Origin));
2391 }
2392 
IMPLEMENT_FUNCTION(VEntity,SlideMove)2393 IMPLEMENT_FUNCTION(VEntity, SlideMove)
2394 {
2395 	P_GET_FLOAT(StepVelScale);
2396 	P_GET_SELF;
2397 	Self->SlideMove(StepVelScale);
2398 }
2399 
IMPLEMENT_FUNCTION(VEntity,BounceWall)2400 IMPLEMENT_FUNCTION(VEntity, BounceWall)
2401 {
2402 	P_GET_FLOAT(overbounce);
2403 	P_GET_FLOAT(bouncefactor);
2404 	P_GET_SELF;
2405 	Self->BounceWall(overbounce, bouncefactor);
2406 }
2407 
IMPLEMENT_FUNCTION(VEntity,UpdateVelocity)2408 IMPLEMENT_FUNCTION(VEntity, UpdateVelocity)
2409 {
2410 	P_GET_SELF;
2411 	Self->UpdateVelocity();
2412 }
2413 
IMPLEMENT_FUNCTION(VEntity,CheckOnmobj)2414 IMPLEMENT_FUNCTION(VEntity, CheckOnmobj)
2415 {
2416 	P_GET_SELF;
2417 	RET_REF(Self->CheckOnmobj());
2418 }
2419 
IMPLEMENT_FUNCTION(VEntity,LinkToWorld)2420 IMPLEMENT_FUNCTION(VEntity, LinkToWorld)
2421 {
2422 	P_GET_SELF;
2423 	Self->LinkToWorld();
2424 }
2425 
IMPLEMENT_FUNCTION(VEntity,UnlinkFromWorld)2426 IMPLEMENT_FUNCTION(VEntity, UnlinkFromWorld)
2427 {
2428 	P_GET_SELF;
2429 	Self->UnlinkFromWorld();
2430 }
2431 
IMPLEMENT_FUNCTION(VEntity,CanSee)2432 IMPLEMENT_FUNCTION(VEntity, CanSee)
2433 {
2434 	P_GET_REF(VEntity, Other);
2435 	P_GET_SELF;
2436 	RET_BOOL(Self->CanSee(Other));
2437 }
2438 
IMPLEMENT_FUNCTION(VEntity,RoughBlockSearch)2439 IMPLEMENT_FUNCTION(VEntity, RoughBlockSearch)
2440 {
2441 	P_GET_INT(Distance);
2442 	P_GET_PTR(VEntity*, EntPtr);
2443 	P_GET_SELF;
2444 	RET_PTR(new VRoughBlockSearchIterator(Self, Distance, EntPtr));
2445 }
2446