1 
2 //**************************************************************************
3 //**
4 //** PO_MAN.C : Heretic 2 : Raven Software, Corp.
5 //**
6 //** $RCSfile: po_man.c,v $
7 //** $Revision: 1.22 $
8 //** $Date: 95/09/28 18:20:56 $
9 //** $Author: cjr $
10 //**
11 //**************************************************************************
12 
13 // HEADER FILES ------------------------------------------------------------
14 
15 #include "doomdef.h"
16 #include "p_local.h"
17 #include "i_system.h"
18 #include "w_wad.h"
19 #include "m_swap.h"
20 #include "m_bbox.h"
21 #include "tables.h"
22 #include "s_sndseq.h"
23 #include "a_sharedglobal.h"
24 #include "p_3dmidtex.h"
25 #include "p_lnspec.h"
26 #include "r_data/r_interpolate.h"
27 #include "g_level.h"
28 #include "po_man.h"
29 #include "p_setup.h"
30 #include "vectors.h"
31 #include "farchive.h"
32 
33 // MACROS ------------------------------------------------------------------
34 
35 #define PO_MAXPOLYSEGS 64
36 
37 // TYPES -------------------------------------------------------------------
38 
V1() const39 inline vertex_t *side_t::V1() const
40 {
41 	return this == linedef->sidedef[0]? linedef->v1 : linedef->v2;
42 }
43 
V2() const44 inline vertex_t *side_t::V2() const
45 {
46 	return this == linedef->sidedef[0]? linedef->v2 : linedef->v1;
47 }
48 
49 
operator <<(FArchive & arc,FPolyObj * & poly)50 FArchive &operator<< (FArchive &arc, FPolyObj *&poly)
51 {
52 	return arc.SerializePointer (polyobjs, (BYTE **)&poly, sizeof(FPolyObj));
53 }
54 
operator <<(FArchive & arc,const FPolyObj * & poly)55 FArchive &operator<< (FArchive &arc, const FPolyObj *&poly)
56 {
57 	return arc.SerializePointer (polyobjs, (BYTE **)&poly, sizeof(FPolyObj));
58 }
59 
operator <<(FArchive & arc,podoortype_t & type)60 inline FArchive &operator<< (FArchive &arc, podoortype_t &type)
61 {
62 	BYTE val = (BYTE)type;
63 	arc << val;
64 	type = (podoortype_t)val;
65 	return arc;
66 }
67 
68 class DPolyAction : public DThinker
69 {
70 	DECLARE_CLASS (DPolyAction, DThinker)
71 	HAS_OBJECT_POINTERS
72 public:
73 	DPolyAction (int polyNum);
74 	void Serialize (FArchive &arc);
75 	void Destroy();
76 	void Stop();
GetSpeed() const77 	int GetSpeed() const { return m_Speed; }
78 
79 	void StopInterpolation ();
80 protected:
81 	DPolyAction ();
82 	int m_PolyObj;
83 	int m_Speed;
84 	int m_Dist;
85 	TObjPtr<DInterpolation> m_Interpolation;
86 
87 	void SetInterpolation ();
88 };
89 
90 class DRotatePoly : public DPolyAction
91 {
92 	DECLARE_CLASS (DRotatePoly, DPolyAction)
93 public:
94 	DRotatePoly (int polyNum);
95 	void Tick ();
96 private:
97 	DRotatePoly ();
98 
99 	friend bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle, int direction, bool overRide);
100 };
101 
102 
103 class DMovePoly : public DPolyAction
104 {
105 	DECLARE_CLASS (DMovePoly, DPolyAction)
106 public:
107 	DMovePoly (int polyNum);
108 	void Serialize (FArchive &arc);
109 	void Tick ();
110 protected:
111 	DMovePoly ();
112 	int m_Angle;
113 	fixed_t m_xSpeed; // for sliding walls
114 	fixed_t m_ySpeed;
115 
116 	friend bool EV_MovePoly (line_t *line, int polyNum, int speed, angle_t angle, fixed_t dist, bool overRide);
117 };
118 
119 class DMovePolyTo : public DPolyAction
120 {
121 	DECLARE_CLASS(DMovePolyTo, DPolyAction)
122 public:
123 	DMovePolyTo(int polyNum);
124 	void Serialize(FArchive &arc);
125 	void Tick();
126 protected:
127 	DMovePolyTo();
128 	fixed_t m_xSpeed;
129 	fixed_t m_ySpeed;
130 	fixed_t m_xTarget;
131 	fixed_t m_yTarget;
132 
133 	friend bool EV_MovePolyTo(line_t *line, int polyNum, int speed, fixed_t x, fixed_t y, bool overRide);
134 };
135 
136 
137 class DPolyDoor : public DMovePoly
138 {
139 	DECLARE_CLASS (DPolyDoor, DMovePoly)
140 public:
141 	DPolyDoor (int polyNum, podoortype_t type);
142 	void Serialize (FArchive &arc);
143 	void Tick ();
144 protected:
145 	int m_Direction;
146 	int m_TotalDist;
147 	int m_Tics;
148 	int m_WaitTics;
149 	podoortype_t m_Type;
150 	bool m_Close;
151 
152 	friend bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle, int delay, int distance, podoortype_t type);
153 private:
154 	DPolyDoor ();
155 };
156 
157 class FPolyMirrorIterator
158 {
159 	FPolyObj *CurPoly;
160 	int UsedPolys[100];	// tracks mirrored polyobjects we've seen
161 	int NumUsedPolys;
162 
163 public:
164 	FPolyMirrorIterator(FPolyObj *poly);
165 	FPolyObj *NextMirror();
166 };
167 
168 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
169 
170 void PO_Init (void);
171 
172 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
173 
174 static void RotatePt (int an, fixed_t *x, fixed_t *y, fixed_t startSpotX,
175 	fixed_t startSpotY);
176 static void UnLinkPolyobj (FPolyObj *po);
177 static void LinkPolyobj (FPolyObj *po);
178 static bool CheckMobjBlocking (side_t *seg, FPolyObj *po);
179 static void InitBlockMap (void);
180 static void IterFindPolySides (FPolyObj *po, side_t *side);
181 static void SpawnPolyobj (int index, int tag, int type);
182 static void TranslateToStartSpot (int tag, int originX, int originY);
183 static void DoMovePolyobj (FPolyObj *po, int x, int y);
184 static void InitSegLists ();
185 static void KillSegLists ();
186 static FPolyNode *NewPolyNode();
187 static void FreePolyNode();
188 static void ReleaseAllPolyNodes();
189 
190 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
191 
192 extern seg_t *segs;
193 
194 // PUBLIC DATA DEFINITIONS -------------------------------------------------
195 
196 polyblock_t **PolyBlockMap;
197 FPolyObj *polyobjs; // list of all poly-objects on the level
198 int po_NumPolyobjs;
199 polyspawns_t *polyspawns; // [RH] Let P_SpawnMapThings() find our thingies for us
200 
201 // PRIVATE DATA DEFINITIONS ------------------------------------------------
202 
203 static TArray<SDWORD> KnownPolySides;
204 static FPolyNode *FreePolyNodes;
205 
206 // CODE --------------------------------------------------------------------
207 
208 
209 
210 //==========================================================================
211 //
212 //
213 //
214 //==========================================================================
215 
216 IMPLEMENT_POINTY_CLASS (DPolyAction)
DECLARE_POINTER(m_Interpolation)217 	DECLARE_POINTER(m_Interpolation)
218 END_POINTERS
219 
220 DPolyAction::DPolyAction ()
221 {
222 }
223 
Serialize(FArchive & arc)224 void DPolyAction::Serialize (FArchive &arc)
225 {
226 	Super::Serialize (arc);
227 	arc << m_PolyObj << m_Speed << m_Dist << m_Interpolation;
228 }
229 
DPolyAction(int polyNum)230 DPolyAction::DPolyAction (int polyNum)
231 {
232 	m_PolyObj = polyNum;
233 	m_Speed = 0;
234 	m_Dist = 0;
235 	SetInterpolation ();
236 }
237 
Destroy()238 void DPolyAction::Destroy()
239 {
240 	FPolyObj *poly = PO_GetPolyobj (m_PolyObj);
241 
242 	if (poly->specialdata == this)
243 	{
244 		poly->specialdata = NULL;
245 	}
246 
247 	StopInterpolation();
248 	Super::Destroy();
249 }
250 
Stop()251 void DPolyAction::Stop()
252 {
253 	FPolyObj *poly = PO_GetPolyobj(m_PolyObj);
254 	SN_StopSequence(poly);
255 	Destroy();
256 }
257 
SetInterpolation()258 void DPolyAction::SetInterpolation ()
259 {
260 	FPolyObj *poly = PO_GetPolyobj (m_PolyObj);
261 	m_Interpolation = poly->SetInterpolation();
262 }
263 
StopInterpolation()264 void DPolyAction::StopInterpolation ()
265 {
266 	if (m_Interpolation != NULL)
267 	{
268 		m_Interpolation->DelRef();
269 		m_Interpolation = NULL;
270 	}
271 }
272 
273 //==========================================================================
274 //
275 //
276 //
277 //==========================================================================
278 
IMPLEMENT_CLASS(DRotatePoly)279 IMPLEMENT_CLASS (DRotatePoly)
280 
281 DRotatePoly::DRotatePoly ()
282 {
283 }
284 
DRotatePoly(int polyNum)285 DRotatePoly::DRotatePoly (int polyNum)
286 	: Super (polyNum)
287 {
288 }
289 
290 //==========================================================================
291 //
292 //
293 //
294 //==========================================================================
295 
IMPLEMENT_CLASS(DMovePoly)296 IMPLEMENT_CLASS (DMovePoly)
297 
298 DMovePoly::DMovePoly ()
299 {
300 }
301 
Serialize(FArchive & arc)302 void DMovePoly::Serialize (FArchive &arc)
303 {
304 	Super::Serialize (arc);
305 	arc << m_Angle << m_xSpeed << m_ySpeed;
306 }
307 
DMovePoly(int polyNum)308 DMovePoly::DMovePoly (int polyNum)
309 	: Super (polyNum)
310 {
311 	m_Angle = 0;
312 	m_xSpeed = 0;
313 	m_ySpeed = 0;
314 }
315 
316 //==========================================================================
317 //
318 //
319 //
320 //
321 //==========================================================================
322 
IMPLEMENT_CLASS(DMovePolyTo)323 IMPLEMENT_CLASS(DMovePolyTo)
324 
325 DMovePolyTo::DMovePolyTo()
326 {
327 }
328 
Serialize(FArchive & arc)329 void DMovePolyTo::Serialize(FArchive &arc)
330 {
331 	Super::Serialize(arc);
332 	arc << m_xSpeed << m_ySpeed << m_xTarget << m_yTarget;
333 }
334 
DMovePolyTo(int polyNum)335 DMovePolyTo::DMovePolyTo(int polyNum)
336 	: Super(polyNum)
337 {
338 	m_xSpeed = 0;
339 	m_ySpeed = 0;
340 	m_xTarget = 0;
341 	m_yTarget = 0;
342 }
343 
344 //==========================================================================
345 //
346 //
347 //
348 //==========================================================================
349 
IMPLEMENT_CLASS(DPolyDoor)350 IMPLEMENT_CLASS (DPolyDoor)
351 
352 DPolyDoor::DPolyDoor ()
353 {
354 }
355 
Serialize(FArchive & arc)356 void DPolyDoor::Serialize (FArchive &arc)
357 {
358 	Super::Serialize (arc);
359 	arc << m_Direction << m_TotalDist << m_Tics << m_WaitTics << m_Type << m_Close;
360 }
361 
DPolyDoor(int polyNum,podoortype_t type)362 DPolyDoor::DPolyDoor (int polyNum, podoortype_t type)
363 	: Super (polyNum), m_Type (type)
364 {
365 	m_Direction = 0;
366 	m_TotalDist = 0;
367 	m_Tics = 0;
368 	m_WaitTics = 0;
369 	m_Close = false;
370 }
371 
372 // ===== Polyobj Event Code =====
373 
374 //==========================================================================
375 //
376 // T_RotatePoly
377 //
378 //==========================================================================
379 
Tick()380 void DRotatePoly::Tick ()
381 {
382 	FPolyObj *poly = PO_GetPolyobj (m_PolyObj);
383 	if (poly == NULL) return;
384 
385 	// Don't let non-perpetual polyobjs overshoot their targets.
386 	if (m_Dist != -1 && (unsigned int)m_Dist < (unsigned int)abs(m_Speed))
387 	{
388 		m_Speed = m_Speed < 0 ? -m_Dist : m_Dist;
389 	}
390 
391 	if (poly->RotatePolyobj (m_Speed))
392 	{
393 		if (m_Dist == -1)
394 		{ // perpetual polyobj
395 			return;
396 		}
397 		m_Dist -= abs(m_Speed);
398 		if (m_Dist == 0)
399 		{
400 			SN_StopSequence (poly);
401 			Destroy ();
402 		}
403 	}
404 }
405 
406 //==========================================================================
407 //
408 // EV_RotatePoly
409 //
410 //==========================================================================
411 
412 
EV_RotatePoly(line_t * line,int polyNum,int speed,int byteAngle,int direction,bool overRide)413 bool EV_RotatePoly (line_t *line, int polyNum, int speed, int byteAngle,
414 					int direction, bool overRide)
415 {
416 	DRotatePoly *pe = NULL;
417 	FPolyObj *poly;
418 
419 	if ((poly = PO_GetPolyobj(polyNum)) == NULL)
420 	{
421 		Printf("EV_RotatePoly: Invalid polyobj num: %d\n", polyNum);
422 		return false;
423 	}
424 	FPolyMirrorIterator it(poly);
425 
426 	while ((poly = it.NextMirror()) != NULL)
427 	{
428 		if (poly->specialdata != NULL && !overRide)
429 		{ // poly is already in motion
430 			break;
431 		}
432 		pe = new DRotatePoly(poly->tag);
433 		poly->specialdata = pe;
434 		if (byteAngle != 0)
435 		{
436 			if (byteAngle == 255)
437 			{
438 				pe->m_Dist = ~0;
439 			}
440 			else
441 			{
442 				pe->m_Dist = byteAngle*(ANGLE_90/64); // Angle
443 			}
444 		}
445 		else
446 		{
447 			pe->m_Dist = ANGLE_MAX-1;
448 		}
449 		pe->m_Speed = speed*direction*(ANGLE_90/(64<<3));
450 		SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
451 		direction = -direction;	// Reverse the direction
452 	}
453 	return pe != NULL;	// Return true if something started moving.
454 }
455 
456 //==========================================================================
457 //
458 // T_MovePoly
459 //
460 //==========================================================================
461 
Tick()462 void DMovePoly::Tick ()
463 {
464 	FPolyObj *poly = PO_GetPolyobj (m_PolyObj);
465 
466 	if (poly != NULL)
467 	{
468 		if (poly->MovePolyobj (m_xSpeed, m_ySpeed))
469 		{
470 			int absSpeed = abs (m_Speed);
471 			m_Dist -= absSpeed;
472 			if (m_Dist <= 0)
473 			{
474 				SN_StopSequence (poly);
475 				Destroy ();
476 			}
477 			else if (m_Dist < absSpeed)
478 			{
479 				m_Speed = m_Dist * (m_Speed < 0 ? -1 : 1);
480 				m_xSpeed = FixedMul (m_Speed, finecosine[m_Angle]);
481 				m_ySpeed = FixedMul (m_Speed, finesine[m_Angle]);
482 			}
483 		}
484 	}
485 }
486 
487 //==========================================================================
488 //
489 // EV_MovePoly
490 //
491 //==========================================================================
492 
EV_MovePoly(line_t * line,int polyNum,int speed,angle_t angle,fixed_t dist,bool overRide)493 bool EV_MovePoly (line_t *line, int polyNum, int speed, angle_t angle,
494 				  fixed_t dist, bool overRide)
495 {
496 	DMovePoly *pe = NULL;
497 	FPolyObj *poly;
498 	angle_t an = angle;
499 
500 	if ((poly = PO_GetPolyobj(polyNum)) == NULL)
501 	{
502 		Printf("EV_MovePoly: Invalid polyobj num: %d\n", polyNum);
503 		return false;
504 	}
505 	FPolyMirrorIterator it(poly);
506 
507 	while ((poly = it.NextMirror()) != NULL)
508 	{
509 		if (poly->specialdata != NULL && !overRide)
510 		{ // poly is already in motion
511 			break;
512 		}
513 		pe = new DMovePoly(poly->tag);
514 		poly->specialdata = pe;
515 		pe->m_Dist = dist; // Distance
516 		pe->m_Speed = speed;
517 		pe->m_Angle = an >> ANGLETOFINESHIFT;
518 		pe->m_xSpeed = FixedMul (pe->m_Speed, finecosine[pe->m_Angle]);
519 		pe->m_ySpeed = FixedMul (pe->m_Speed, finesine[pe->m_Angle]);
520 		SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
521 
522 		// Do not interpolate very fast moving polyobjects. The minimum tic count is
523 		// 3 instead of 2, because the moving crate effect in Massmouth 2, Hostitality
524 		// that this fixes isn't quite fast enough to move the crate back to its start
525 		// in just 1 tic.
526 		if (dist/speed <= 2)
527 		{
528 			pe->StopInterpolation ();
529 		}
530 
531 		an = an + ANGLE_180;	// Reverse the angle.
532 	}
533 	return pe != NULL;	// Return true if something started moving.
534 }
535 
536 //==========================================================================
537 //
538 // DMovePolyTo :: Tick
539 //
540 //==========================================================================
541 
Tick()542 void DMovePolyTo::Tick ()
543 {
544 	FPolyObj *poly = PO_GetPolyobj (m_PolyObj);
545 
546 	if (poly != NULL)
547 	{
548 		if (poly->MovePolyobj (m_xSpeed, m_ySpeed))
549 		{
550 			int absSpeed = abs (m_Speed);
551 			m_Dist -= absSpeed;
552 			if (m_Dist <= 0)
553 			{
554 				SN_StopSequence (poly);
555 				Destroy ();
556 			}
557 			else if (m_Dist < absSpeed)
558 			{
559 				m_Speed = m_Dist * (m_Speed < 0 ? -1 : 1);
560 				m_xSpeed = m_xTarget - poly->StartSpot.x;
561 				m_ySpeed = m_yTarget - poly->StartSpot.y;
562 			}
563 		}
564 	}
565 }
566 
567 //==========================================================================
568 //
569 // EV_MovePolyTo
570 //
571 //==========================================================================
572 
EV_MovePolyTo(line_t * line,int polyNum,int speed,fixed_t targx,fixed_t targy,bool overRide)573 bool EV_MovePolyTo(line_t *line, int polyNum, int speed, fixed_t targx, fixed_t targy, bool overRide)
574 {
575 	DMovePolyTo *pe = NULL;
576 	FPolyObj *poly;
577 	TVector2<double> dist;
578 	double distlen;
579 
580 	if ((poly = PO_GetPolyobj(polyNum)) == NULL)
581 	{
582 		Printf("EV_MovePolyTo: Invalid polyobj num: %d\n", polyNum);
583 		return false;
584 	}
585 	FPolyMirrorIterator it(poly);
586 
587 	dist.X = targx - poly->StartSpot.x;
588 	dist.Y = targy - poly->StartSpot.y;
589 	distlen = dist.MakeUnit();
590 	while ((poly = it.NextMirror()) != NULL)
591 	{
592 		if (poly->specialdata != NULL && !overRide)
593 		{ // poly is already in motion
594 			break;
595 		}
596 		pe = new DMovePolyTo(poly->tag);
597 		poly->specialdata = pe;
598 		pe->m_Dist = xs_RoundToInt(distlen);
599 		pe->m_Speed = speed;
600 		pe->m_xSpeed = xs_RoundToInt(speed * dist.X);
601 		pe->m_ySpeed = xs_RoundToInt(speed * dist.Y);
602 		pe->m_xTarget = xs_RoundToInt(poly->StartSpot.x + distlen * dist.X);
603 		pe->m_yTarget = xs_RoundToInt(poly->StartSpot.y + distlen * dist.Y);
604 		if ((pe->m_Dist / pe->m_Speed) <= 2)
605 		{
606 			pe->StopInterpolation();
607 		}
608 		dist = -dist;	// reverse the direction
609 	}
610 	return pe != NULL; // Return true if something started moving.
611 }
612 
613 //==========================================================================
614 //
615 // T_PolyDoor
616 //
617 //==========================================================================
618 
Tick()619 void DPolyDoor::Tick ()
620 {
621 	int absSpeed;
622 	FPolyObj *poly = PO_GetPolyobj (m_PolyObj);
623 
624 	if (poly == NULL) return;
625 
626 	if (m_Tics)
627 	{
628 		if (!--m_Tics)
629 		{
630 			SN_StartSequence (poly, poly->seqType, SEQ_DOOR, m_Close);
631 		}
632 		return;
633 	}
634 	switch (m_Type)
635 	{
636 	case PODOOR_SLIDE:
637 		if (m_Dist <= 0 || poly->MovePolyobj (m_xSpeed, m_ySpeed))
638 		{
639 			absSpeed = abs (m_Speed);
640 			m_Dist -= absSpeed;
641 			if (m_Dist <= 0)
642 			{
643 				SN_StopSequence (poly);
644 				if (!m_Close)
645 				{
646 					m_Dist = m_TotalDist;
647 					m_Close = true;
648 					m_Tics = m_WaitTics;
649 					m_Direction = (ANGLE_MAX>>ANGLETOFINESHIFT) - m_Direction;
650 					m_xSpeed = -m_xSpeed;
651 					m_ySpeed = -m_ySpeed;
652 				}
653 				else
654 				{
655 					Destroy ();
656 				}
657 			}
658 		}
659 		else
660 		{
661 			if (poly->crush || !m_Close)
662 			{ // continue moving if the poly is a crusher, or is opening
663 				return;
664 			}
665 			else
666 			{ // open back up
667 				m_Dist = m_TotalDist - m_Dist;
668 				m_Direction = (ANGLE_MAX>>ANGLETOFINESHIFT)-
669 					m_Direction;
670 				m_xSpeed = -m_xSpeed;
671 				m_ySpeed = -m_ySpeed;
672 				m_Close = false;
673 				SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
674 			}
675 		}
676 		break;
677 
678 	case PODOOR_SWING:
679 		if (m_Dist <= 0 || poly->RotatePolyobj (m_Speed))
680 		{
681 			absSpeed = abs (m_Speed);
682 			m_Dist -= absSpeed;
683 			if (m_Dist <= 0)
684 			{
685 				SN_StopSequence (poly);
686 				if (!m_Close)
687 				{
688 					m_Dist = m_TotalDist;
689 					m_Close = true;
690 					m_Tics = m_WaitTics;
691 					m_Speed = -m_Speed;
692 				}
693 				else
694 				{
695 					Destroy ();
696 				}
697 			}
698 		}
699 		else
700 		{
701 			if(poly->crush || !m_Close)
702 			{ // continue moving if the poly is a crusher, or is opening
703 				return;
704 			}
705 			else
706 			{ // open back up and rewait
707 				m_Dist = m_TotalDist - m_Dist;
708 				m_Speed = -m_Speed;
709 				m_Close = false;
710 				SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
711 			}
712 		}
713 		break;
714 
715 	default:
716 		break;
717 	}
718 }
719 
720 //==========================================================================
721 //
722 // EV_OpenPolyDoor
723 //
724 //==========================================================================
725 
EV_OpenPolyDoor(line_t * line,int polyNum,int speed,angle_t angle,int delay,int distance,podoortype_t type)726 bool EV_OpenPolyDoor (line_t *line, int polyNum, int speed, angle_t angle,
727 					  int delay, int distance, podoortype_t type)
728 {
729 	DPolyDoor *pd = NULL;
730 	FPolyObj *poly;
731 	int swingdir = 1;	// ADD:  PODOOR_SWINGL, PODOOR_SWINGR
732 
733 	if ((poly = PO_GetPolyobj(polyNum)) == NULL)
734 	{
735 		Printf("EV_OpenPolyDoor: Invalid polyobj num: %d\n", polyNum);
736 		return false;
737 	}
738 	FPolyMirrorIterator it(poly);
739 
740 	while ((poly = it.NextMirror()) != NULL)
741 	{
742 		if (poly->specialdata != NULL)
743 		{ // poly is already moving
744 			break;
745 		}
746 		pd = new DPolyDoor(poly->tag, type);
747 		poly->specialdata = pd;
748 		if (type == PODOOR_SLIDE)
749 		{
750 			pd->m_WaitTics = delay;
751 			pd->m_Speed = speed;
752 			pd->m_Dist = pd->m_TotalDist = distance; // Distance
753 			pd->m_Direction = angle >> ANGLETOFINESHIFT;
754 			pd->m_xSpeed = FixedMul (pd->m_Speed, finecosine[pd->m_Direction]);
755 			pd->m_ySpeed = FixedMul (pd->m_Speed, finesine[pd->m_Direction]);
756 			SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
757 			angle += ANGLE_180;	// reverse the angle
758 		}
759 		else if (type == PODOOR_SWING)
760 		{
761 			pd->m_WaitTics = delay;
762 			pd->m_Direction = swingdir;
763 			pd->m_Speed = (speed*pd->m_Direction*(ANGLE_90/64))>>3;
764 			pd->m_Dist = pd->m_TotalDist = angle;
765 			SN_StartSequence (poly, poly->seqType, SEQ_DOOR, 0);
766 			swingdir = -swingdir;	// reverse the direction
767 		}
768 
769 	}
770 	return pd != NULL;	// Return true if something started moving.
771 }
772 
773 //==========================================================================
774 //
775 // EV_StopPoly
776 //
777 //==========================================================================
778 
EV_StopPoly(int polynum)779 bool EV_StopPoly(int polynum)
780 {
781 	FPolyObj *poly;
782 
783 	if (NULL != (poly = PO_GetPolyobj(polynum)))
784 	{
785 		if (poly->specialdata != NULL)
786 		{
787 			poly->specialdata->Stop();
788 		}
789 		return true;
790 	}
791 	return false;
792 }
793 
794 // ===== Higher Level Poly Interface code =====
795 
796 //==========================================================================
797 //
798 // PO_GetPolyobj
799 //
800 //==========================================================================
801 
PO_GetPolyobj(int polyNum)802 FPolyObj *PO_GetPolyobj (int polyNum)
803 {
804 	int i;
805 
806 	for (i = 0; i < po_NumPolyobjs; i++)
807 	{
808 		if (polyobjs[i].tag == polyNum)
809 		{
810 			return &polyobjs[i];
811 		}
812 	}
813 	return NULL;
814 }
815 
816 
817 //==========================================================================
818 //
819 //
820 //
821 //==========================================================================
822 
FPolyObj()823 FPolyObj::FPolyObj()
824 {
825 	StartSpot.x = StartSpot.y = 0;
826 	angle = 0;
827 	tag = 0;
828 	memset(bbox, 0, sizeof(bbox));
829 	validcount = 0;
830 	crush = 0;
831 	bHurtOnTouch = false;
832 	seqType = 0;
833 	size = 0;
834 	subsectorlinks = NULL;
835 	specialdata = NULL;
836 	interpolation = NULL;
837 }
838 
839 //==========================================================================
840 //
841 // GetPolyobjMirror
842 //
843 //==========================================================================
844 
GetMirror()845 int FPolyObj::GetMirror()
846 {
847 	return MirrorNum;
848 }
849 
850 //==========================================================================
851 //
852 // ThrustMobj
853 //
854 //==========================================================================
855 
ThrustMobj(AActor * actor,side_t * side)856 void FPolyObj::ThrustMobj (AActor *actor, side_t *side)
857 {
858 	int thrustAngle;
859 	int thrustX;
860 	int thrustY;
861 	DPolyAction *pe;
862 
863 	int force;
864 
865 	if (!(actor->flags&MF_SHOOTABLE) && !actor->player)
866 	{
867 		return;
868 	}
869 	vertex_t *v1 = side->V1();
870 	vertex_t *v2 = side->V2();
871 	thrustAngle = (R_PointToAngle2 (v1->x, v1->y, v2->x, v2->y) - ANGLE_90) >> ANGLETOFINESHIFT;
872 
873 	pe = static_cast<DPolyAction *>(specialdata);
874 	if (pe)
875 	{
876 		if (pe->IsKindOf (RUNTIME_CLASS (DRotatePoly)))
877 		{
878 			force = pe->GetSpeed() >> 8;
879 		}
880 		else
881 		{
882 			force = pe->GetSpeed() >> 3;
883 		}
884 		if (force < FRACUNIT)
885 		{
886 			force = FRACUNIT;
887 		}
888 		else if (force > 4*FRACUNIT)
889 		{
890 			force = 4*FRACUNIT;
891 		}
892 	}
893 	else
894 	{
895 		force = FRACUNIT;
896 	}
897 
898 	thrustX = FixedMul (force, finecosine[thrustAngle]);
899 	thrustY = FixedMul (force, finesine[thrustAngle]);
900 	actor->velx += thrustX;
901 	actor->vely += thrustY;
902 	if (crush)
903 	{
904 		fixedvec2 pos = actor->Vec2Offset(thrustX, thrustY);
905 		if (bHurtOnTouch || !P_CheckMove (actor, pos.x, pos.y))
906 		{
907 			int newdam = P_DamageMobj (actor, NULL, NULL, crush, NAME_Crush);
908 			P_TraceBleed (newdam > 0 ? newdam : crush, actor);
909 		}
910 	}
911 	if (level.flags2 & LEVEL2_POLYGRIND) actor->Grind(false); // crush corpses that get caught in a polyobject's way
912 }
913 
914 //==========================================================================
915 //
916 // UpdateSegBBox
917 //
918 //==========================================================================
919 
UpdateBBox()920 void FPolyObj::UpdateBBox ()
921 {
922 	for(unsigned i=0;i<Linedefs.Size(); i++)
923 	{
924 		line_t *line = Linedefs[i];
925 
926 		if (line->v1->x < line->v2->x)
927 		{
928 			line->bbox[BOXLEFT] = line->v1->x;
929 			line->bbox[BOXRIGHT] = line->v2->x;
930 		}
931 		else
932 		{
933 			line->bbox[BOXLEFT] = line->v2->x;
934 			line->bbox[BOXRIGHT] = line->v1->x;
935 		}
936 		if (line->v1->y < line->v2->y)
937 		{
938 			line->bbox[BOXBOTTOM] = line->v1->y;
939 			line->bbox[BOXTOP] = line->v2->y;
940 		}
941 		else
942 		{
943 			line->bbox[BOXBOTTOM] = line->v2->y;
944 			line->bbox[BOXTOP] = line->v1->y;
945 		}
946 
947 		// Update the line's slopetype
948 		line->dx = line->v2->x - line->v1->x;
949 		line->dy = line->v2->y - line->v1->y;
950 	}
951 	CalcCenter();
952 }
953 
CalcCenter()954 void FPolyObj::CalcCenter()
955 {
956 	SQWORD cx = 0, cy = 0;
957 	for(unsigned i=0;i<Vertices.Size(); i++)
958 	{
959 		cx += Vertices[i]->x;
960 		cy += Vertices[i]->y;
961 	}
962 	CenterSpot.x = (fixed_t)(cx / Vertices.Size());
963 	CenterSpot.y = (fixed_t)(cy / Vertices.Size());
964 }
965 
966 //==========================================================================
967 //
968 // PO_MovePolyobj
969 //
970 //==========================================================================
971 
MovePolyobj(int x,int y,bool force)972 bool FPolyObj::MovePolyobj (int x, int y, bool force)
973 {
974 	FBoundingBox oldbounds = Bounds;
975 	UnLinkPolyobj ();
976 	DoMovePolyobj (x, y);
977 
978 	if (!force)
979 	{
980 		bool blocked = false;
981 
982 		for(unsigned i=0;i < Sidedefs.Size(); i++)
983 		{
984 			if (CheckMobjBlocking(Sidedefs[i]))
985 			{
986 				blocked = true;
987 			}
988 		}
989 		if (blocked)
990 		{
991 			DoMovePolyobj (-x, -y);
992 			LinkPolyobj();
993 			return false;
994 		}
995 	}
996 	StartSpot.x += x;
997 	StartSpot.y += y;
998 	CenterSpot.x += x;
999 	CenterSpot.y += y;
1000 	LinkPolyobj ();
1001 	ClearSubsectorLinks();
1002 	RecalcActorFloorCeil(Bounds | oldbounds);
1003 	return true;
1004 }
1005 
1006 //==========================================================================
1007 //
1008 // DoMovePolyobj
1009 //
1010 //==========================================================================
1011 
DoMovePolyobj(int x,int y)1012 void FPolyObj::DoMovePolyobj (int x, int y)
1013 {
1014 	for(unsigned i=0;i < Vertices.Size(); i++)
1015 	{
1016 		Vertices[i]->x += x;
1017 		Vertices[i]->y += y;
1018 		PrevPts[i].x += x;
1019 		PrevPts[i].y += y;
1020 	}
1021 	for (unsigned i = 0; i < Linedefs.Size(); i++)
1022 	{
1023 		Linedefs[i]->bbox[BOXTOP] += y;
1024 		Linedefs[i]->bbox[BOXBOTTOM] += y;
1025 		Linedefs[i]->bbox[BOXLEFT] += x;
1026 		Linedefs[i]->bbox[BOXRIGHT] += x;
1027 	}
1028 }
1029 
1030 //==========================================================================
1031 //
1032 // RotatePt
1033 //
1034 //==========================================================================
1035 
RotatePt(int an,fixed_t * x,fixed_t * y,fixed_t startSpotX,fixed_t startSpotY)1036 static void RotatePt (int an, fixed_t *x, fixed_t *y, fixed_t startSpotX, fixed_t startSpotY)
1037 {
1038 	fixed_t tr_x = *x;
1039 	fixed_t tr_y = *y;
1040 
1041 	*x = (DMulScale16 (tr_x, finecosine[an], -tr_y, finesine[an]) & 0xFFFFFE00) + startSpotX;
1042 	*y = (DMulScale16 (tr_x, finesine[an], tr_y, finecosine[an]) & 0xFFFFFE00) + startSpotY;
1043 }
1044 
1045 //==========================================================================
1046 //
1047 // PO_RotatePolyobj
1048 //
1049 //==========================================================================
1050 
RotatePolyobj(angle_t angle,bool fromsave)1051 bool FPolyObj::RotatePolyobj (angle_t angle, bool fromsave)
1052 {
1053 	int an;
1054 	bool blocked;
1055 	FBoundingBox oldbounds = Bounds;
1056 
1057 	an = (this->angle+angle)>>ANGLETOFINESHIFT;
1058 
1059 	UnLinkPolyobj();
1060 
1061 	for(unsigned i=0;i < Vertices.Size(); i++)
1062 	{
1063 		PrevPts[i].x = Vertices[i]->x;
1064 		PrevPts[i].y = Vertices[i]->y;
1065 		Vertices[i]->x = OriginalPts[i].x;
1066 		Vertices[i]->y = OriginalPts[i].y;
1067 		RotatePt(an, &Vertices[i]->x, &Vertices[i]->y, StartSpot.x,	StartSpot.y);
1068 	}
1069 	blocked = false;
1070 	validcount++;
1071 	UpdateBBox();
1072 
1073 	// If we are loading a savegame we do not really want to damage actors and be blocked by them. This can also cause crashes when trying to damage incompletely deserialized player pawns.
1074 	if (!fromsave)
1075 	{
1076 		for (unsigned i = 0; i < Sidedefs.Size(); i++)
1077 		{
1078 			if (CheckMobjBlocking(Sidedefs[i]))
1079 			{
1080 				blocked = true;
1081 			}
1082 		}
1083 		if (blocked)
1084 		{
1085 			for(unsigned i=0;i < Vertices.Size(); i++)
1086 			{
1087 				Vertices[i]->x = PrevPts[i].x;
1088 				Vertices[i]->y = PrevPts[i].y;
1089 			}
1090 			UpdateBBox();
1091 			LinkPolyobj();
1092 			return false;
1093 		}
1094 	}
1095 	this->angle += angle;
1096 	LinkPolyobj();
1097 	ClearSubsectorLinks();
1098 	RecalcActorFloorCeil(Bounds | oldbounds);
1099 	return true;
1100 }
1101 
1102 //==========================================================================
1103 //
1104 // UnLinkPolyobj
1105 //
1106 //==========================================================================
1107 
UnLinkPolyobj()1108 void FPolyObj::UnLinkPolyobj ()
1109 {
1110 	polyblock_t *link;
1111 	int i, j;
1112 	int index;
1113 
1114 	// remove the polyobj from each blockmap section
1115 	for(j = bbox[BOXBOTTOM]; j <= bbox[BOXTOP]; j++)
1116 	{
1117 		index = j*bmapwidth;
1118 		for(i = bbox[BOXLEFT]; i <= bbox[BOXRIGHT]; i++)
1119 		{
1120 			if(i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight)
1121 			{
1122 				link = PolyBlockMap[index+i];
1123 				while(link != NULL && link->polyobj != this)
1124 				{
1125 					link = link->next;
1126 				}
1127 				if(link == NULL)
1128 				{ // polyobj not located in the link cell
1129 					continue;
1130 				}
1131 				link->polyobj = NULL;
1132 			}
1133 		}
1134 	}
1135 }
1136 
1137 //==========================================================================
1138 //
1139 // CheckMobjBlocking
1140 //
1141 //==========================================================================
1142 
CheckMobjBlocking(side_t * sd)1143 bool FPolyObj::CheckMobjBlocking (side_t *sd)
1144 {
1145 	static TArray<AActor *> checker;
1146 	FBlockNode *block;
1147 	AActor *mobj;
1148 	int i, j, k;
1149 	int left, right, top, bottom;
1150 	line_t *ld;
1151 	bool blocked;
1152 	bool performBlockingThrust;
1153 
1154 	ld = sd->linedef;
1155 
1156 	top = GetSafeBlockY(ld->bbox[BOXTOP]-bmaporgy);
1157 	bottom = GetSafeBlockY(ld->bbox[BOXBOTTOM]-bmaporgy);
1158 	left = GetSafeBlockX(ld->bbox[BOXLEFT]-bmaporgx);
1159 	right = GetSafeBlockX(ld->bbox[BOXRIGHT]-bmaporgx);
1160 
1161 	blocked = false;
1162 	checker.Clear();
1163 
1164 	bottom = bottom < 0 ? 0 : bottom;
1165 	bottom = bottom >= bmapheight ? bmapheight-1 : bottom;
1166 	top = top < 0 ? 0 : top;
1167 	top = top >= bmapheight  ? bmapheight-1 : top;
1168 	left = left < 0 ? 0 : left;
1169 	left = left >= bmapwidth ? bmapwidth-1 : left;
1170 	right = right < 0 ? 0 : right;
1171 	right = right >= bmapwidth ?  bmapwidth-1 : right;
1172 
1173 	for (j = bottom*bmapwidth; j <= top*bmapwidth; j += bmapwidth)
1174 	{
1175 		for (i = left; i <= right; i++)
1176 		{
1177 			for (block = blocklinks[j+i]; block != NULL; block = block->NextActor)
1178 			{
1179 				mobj = block->Me;
1180 				for (k = (int)checker.Size()-1; k >= 0; --k)
1181 				{
1182 					if (checker[k] == mobj)
1183 					{
1184 						break;
1185 					}
1186 				}
1187 				if (k < 0)
1188 				{
1189 					checker.Push (mobj);
1190 					if ((mobj->flags&MF_SOLID) && !(mobj->flags&MF_NOCLIP))
1191 					{
1192 						FLineOpening open;
1193 						open.top = INT_MAX;
1194 						open.bottom = -INT_MAX;
1195 						// [TN] Check wether this actor gets blocked by the line.
1196 						if (ld->backsector != NULL &&
1197 							!(ld->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING))
1198 							&& !(ld->flags & ML_BLOCK_PLAYERS && mobj->player)
1199 							&& !(ld->flags & ML_BLOCKMONSTERS && mobj->flags3 & MF3_ISMONSTER)
1200 							&& !((mobj->flags & MF_FLOAT) && (ld->flags & ML_BLOCK_FLOATERS))
1201 							&& (!(ld->flags & ML_3DMIDTEX) ||
1202 								(!P_LineOpening_3dMidtex(mobj, ld, open) &&
1203 									(mobj->Top() < open.top)
1204 								) || (open.abovemidtex && mobj->Z() > mobj->floorz))
1205 							)
1206 						{
1207 							// [BL] We can't just continue here since we must
1208 							// determine if the line's backsector is going to
1209 							// be blocked.
1210 							performBlockingThrust = false;
1211 						}
1212 						else
1213 						{
1214 							performBlockingThrust = true;
1215 						}
1216 
1217 						FBoundingBox box(mobj->X(), mobj->Y(), mobj->radius);
1218 
1219 						if (box.Right() <= ld->bbox[BOXLEFT]
1220 							|| box.Left() >= ld->bbox[BOXRIGHT]
1221 							|| box.Top() <= ld->bbox[BOXBOTTOM]
1222 							|| box.Bottom() >= ld->bbox[BOXTOP])
1223 						{
1224 							continue;
1225 						}
1226 						if (box.BoxOnLineSide(ld) != -1)
1227 						{
1228 							continue;
1229 						}
1230 						// We have a two-sided linedef so we should only check one side
1231 						// so that the thrust from both sides doesn't cancel each other out.
1232 						// Best use the one facing the player and ignore the back side.
1233 						if (ld->sidedef[1] != NULL)
1234 						{
1235 							int side = P_PointOnLineSidePrecise(mobj->X(), mobj->Y(), ld);
1236 							if (ld->sidedef[side] != sd)
1237 							{
1238 								continue;
1239 							}
1240 							// [BL] See if we hit below the floor/ceiling of the poly.
1241 							else if(!performBlockingThrust && (
1242 									mobj->Z() < ld->sidedef[!side]->sector->GetSecPlane(sector_t::floor).ZatPoint(mobj) ||
1243 									mobj->Top() > ld->sidedef[!side]->sector->GetSecPlane(sector_t::ceiling).ZatPoint(mobj)
1244 								))
1245 							{
1246 								performBlockingThrust = true;
1247 							}
1248 						}
1249 
1250 						if(performBlockingThrust)
1251 						{
1252 							ThrustMobj (mobj, sd);
1253 							blocked = true;
1254 						}
1255 						else
1256 							continue;
1257 					}
1258 				}
1259 			}
1260 		}
1261 	}
1262 	return blocked;
1263 }
1264 
1265 //==========================================================================
1266 //
1267 // LinkPolyobj
1268 //
1269 //==========================================================================
1270 
LinkPolyobj()1271 void FPolyObj::LinkPolyobj ()
1272 {
1273 	polyblock_t **link;
1274 	polyblock_t *tempLink;
1275 
1276 	// calculate the polyobj bbox
1277 	Bounds.ClearBox();
1278 	for(unsigned i = 0; i < Sidedefs.Size(); i++)
1279 	{
1280 		vertex_t *vt;
1281 
1282 		vt = Sidedefs[i]->linedef->v1;
1283 		Bounds.AddToBox(vt->x, vt->y);
1284 		vt = Sidedefs[i]->linedef->v2;
1285 		Bounds.AddToBox(vt->x, vt->y);
1286 	}
1287 	bbox[BOXRIGHT] = GetSafeBlockX(Bounds.Right() - bmaporgx);
1288 	bbox[BOXLEFT] = GetSafeBlockX(Bounds.Left() - bmaporgx);
1289 	bbox[BOXTOP] = GetSafeBlockY(Bounds.Top() - bmaporgy);
1290 	bbox[BOXBOTTOM] = GetSafeBlockY(Bounds.Bottom() - bmaporgy);
1291 	// add the polyobj to each blockmap section
1292 	for(int j = bbox[BOXBOTTOM]*bmapwidth; j <= bbox[BOXTOP]*bmapwidth;
1293 		j += bmapwidth)
1294 	{
1295 		for(int i = bbox[BOXLEFT]; i <= bbox[BOXRIGHT]; i++)
1296 		{
1297 			if(i >= 0 && i < bmapwidth && j >= 0 && j < bmapheight*bmapwidth)
1298 			{
1299 				link = &PolyBlockMap[j+i];
1300 				if(!(*link))
1301 				{ // Create a new link at the current block cell
1302 					*link = new polyblock_t;
1303 					(*link)->next = NULL;
1304 					(*link)->prev = NULL;
1305 					(*link)->polyobj = this;
1306 					continue;
1307 				}
1308 				else
1309 				{
1310 					tempLink = *link;
1311 					while(tempLink->next != NULL && tempLink->polyobj != NULL)
1312 					{
1313 						tempLink = tempLink->next;
1314 					}
1315 				}
1316 				if(tempLink->polyobj == NULL)
1317 				{
1318 					tempLink->polyobj = this;
1319 					continue;
1320 				}
1321 				else
1322 				{
1323 					tempLink->next = new polyblock_t;
1324 					tempLink->next->next = NULL;
1325 					tempLink->next->prev = tempLink;
1326 					tempLink->next->polyobj = this;
1327 				}
1328 			}
1329 			// else, don't link the polyobj, since it's off the map
1330 		}
1331 	}
1332 }
1333 
1334 //===========================================================================
1335 //
1336 // FPolyObj :: RecalcActorFloorCeil
1337 //
1338 // For each actor within the bounding box, recalculate its floorz, ceilingz,
1339 // and related values.
1340 //
1341 //===========================================================================
1342 
RecalcActorFloorCeil(FBoundingBox bounds) const1343 void FPolyObj::RecalcActorFloorCeil(FBoundingBox bounds) const
1344 {
1345 	FBlockThingsIterator it(bounds);
1346 	AActor *actor;
1347 
1348 	while ((actor = it.Next()) != NULL)
1349 	{
1350 		P_FindFloorCeiling(actor);
1351 	}
1352 }
1353 
1354 //===========================================================================
1355 //
1356 // PO_ClosestPoint
1357 //
1358 // Given a point (x,y), returns the point (ox,oy) on the polyobject's walls
1359 // that is nearest to (x,y). Also returns the seg this point came from.
1360 //
1361 //===========================================================================
1362 
ClosestPoint(fixed_t fx,fixed_t fy,fixed_t & ox,fixed_t & oy,side_t ** side) const1363 void FPolyObj::ClosestPoint(fixed_t fx, fixed_t fy, fixed_t &ox, fixed_t &oy, side_t **side) const
1364 {
1365 	unsigned int i;
1366 	double x = fx, y = fy;
1367 	double bestdist = HUGE_VAL;
1368 	double bestx = 0, besty = 0;
1369 	side_t *bestline = NULL;
1370 
1371 	for (i = 0; i < Sidedefs.Size(); ++i)
1372 	{
1373 		vertex_t *v1 = Sidedefs[i]->V1();
1374 		vertex_t *v2 = Sidedefs[i]->V2();
1375 		double a = v2->x - v1->x;
1376 		double b = v2->y - v1->y;
1377 		double den = a*a + b*b;
1378 		double ix, iy, dist;
1379 
1380 		if (den == 0)
1381 		{ // Line is actually a point!
1382 			ix = v1->x;
1383 			iy = v1->y;
1384 		}
1385 		else
1386 		{
1387 			double num = (x - v1->x) * a + (y - v1->y) * b;
1388 			double u = num / den;
1389 			if (u <= 0)
1390 			{
1391 				ix = v1->x;
1392 				iy = v1->y;
1393 			}
1394 			else if (u >= 1)
1395 			{
1396 				ix = v2->x;
1397 				iy = v2->y;
1398 			}
1399 			else
1400 			{
1401 				ix = v1->x + u * a;
1402 				iy = v1->y + u * b;
1403 			}
1404 		}
1405 		a = (ix - x);
1406 		b = (iy - y);
1407 		dist = a*a + b*b;
1408 		if (dist < bestdist)
1409 		{
1410 			bestdist = dist;
1411 			bestx = ix;
1412 			besty = iy;
1413 			bestline = Sidedefs[i];
1414 		}
1415 	}
1416 	ox = fixed_t(bestx);
1417 	oy = fixed_t(besty);
1418 	if (side != NULL)
1419 	{
1420 		*side = bestline;
1421 	}
1422 }
1423 
1424 //==========================================================================
1425 //
1426 // InitBlockMap
1427 //
1428 //==========================================================================
1429 
InitBlockMap(void)1430 static void InitBlockMap (void)
1431 {
1432 	int i;
1433 
1434 	PolyBlockMap = new polyblock_t *[bmapwidth*bmapheight];
1435 	memset (PolyBlockMap, 0, bmapwidth*bmapheight*sizeof(polyblock_t *));
1436 
1437 	for (i = 0; i < po_NumPolyobjs; i++)
1438 	{
1439 		polyobjs[i].LinkPolyobj();
1440 	}
1441 }
1442 
1443 //==========================================================================
1444 //
1445 // InitSideLists [RH]
1446 //
1447 // Group sides by vertex and collect side that are known to belong to a
1448 // polyobject so that they can be initialized fast.
1449 //==========================================================================
1450 
InitSideLists()1451 static void InitSideLists ()
1452 {
1453 	for (int i = 0; i < numsides; ++i)
1454 	{
1455 		if (sides[i].linedef != NULL &&
1456 			(sides[i].linedef->special == Polyobj_StartLine ||
1457 			 sides[i].linedef->special == Polyobj_ExplicitLine))
1458 		{
1459 			KnownPolySides.Push (i);
1460 		}
1461 	}
1462 }
1463 
1464 //==========================================================================
1465 //
1466 // KillSideLists [RH]
1467 //
1468 //==========================================================================
1469 
KillSideLists()1470 static void KillSideLists ()
1471 {
1472 	KnownPolySides.Clear ();
1473 	KnownPolySides.ShrinkToFit ();
1474 }
1475 
1476 //==========================================================================
1477 //
1478 // AddPolyVert
1479 //
1480 // Helper function for IterFindPolySides()
1481 //
1482 //==========================================================================
1483 
AddPolyVert(TArray<DWORD> & vnum,DWORD vert)1484 static void AddPolyVert(TArray<DWORD> &vnum, DWORD vert)
1485 {
1486 	for (unsigned int i = vnum.Size() - 1; i-- != 0; )
1487 	{
1488 		if (vnum[i] == vert)
1489 		{ // Already in the set. No need to add it.
1490 			return;
1491 		}
1492 	}
1493 	vnum.Push(vert);
1494 }
1495 
1496 //==========================================================================
1497 //
1498 // IterFindPolySides
1499 //
1500 // Beginning with the first vertex of the starting side, for each vertex
1501 // in vnum, add all the sides that use it as a first vertex to the polyobj,
1502 // and add all their second vertices to vnum. This continues until there
1503 // are no new vertices in vnum.
1504 //
1505 //==========================================================================
1506 
IterFindPolySides(FPolyObj * po,side_t * side)1507 static void IterFindPolySides (FPolyObj *po, side_t *side)
1508 {
1509 	static TArray<DWORD> vnum;
1510 	unsigned int vnumat;
1511 
1512 	assert(sidetemp != NULL);
1513 
1514 	vnum.Clear();
1515 	vnum.Push(DWORD(side->V1() - vertexes));
1516 	vnumat = 0;
1517 
1518 	while (vnum.Size() != vnumat)
1519 	{
1520 		DWORD sidenum = sidetemp[vnum[vnumat++]].b.first;
1521 		while (sidenum != NO_SIDE)
1522 		{
1523 			po->Sidedefs.Push(&sides[sidenum]);
1524 			AddPolyVert(vnum, DWORD(sides[sidenum].V2() - vertexes));
1525 			sidenum = sidetemp[sidenum].b.next;
1526 		}
1527 	}
1528 }
1529 
1530 
1531 //==========================================================================
1532 //
1533 // SpawnPolyobj
1534 //
1535 //==========================================================================
1536 
posicmp(const void * a,const void * b)1537 static int STACK_ARGS posicmp(const void *a, const void *b)
1538 {
1539 	return (*(const side_t **)a)->linedef->args[1] - (*(const side_t **)b)->linedef->args[1];
1540 }
1541 
SpawnPolyobj(int index,int tag,int type)1542 static void SpawnPolyobj (int index, int tag, int type)
1543 {
1544 	unsigned int ii;
1545 	int i;
1546 	FPolyObj *po = &polyobjs[index];
1547 
1548 	for (ii = 0; ii < KnownPolySides.Size(); ++ii)
1549 	{
1550 		i = KnownPolySides[ii];
1551 		if (i < 0)
1552 		{
1553 			continue;
1554 		}
1555 
1556 		side_t *sd = &sides[i];
1557 
1558 		if (sd->linedef->special == Polyobj_StartLine &&
1559 			sd->linedef->args[0] == tag)
1560 		{
1561 			if (po->Sidedefs.Size() > 0)
1562 			{
1563 				I_Error ("SpawnPolyobj: Polyobj %d already spawned.\n", tag);
1564 			}
1565 			sd->linedef->special = 0;
1566 			sd->linedef->args[0] = 0;
1567 			IterFindPolySides(&polyobjs[index], sd);
1568 			po->MirrorNum = sd->linedef->args[1];
1569 			po->crush = (type != SMT_PolySpawn) ? 3 : 0;
1570 			po->bHurtOnTouch = (type == SMT_PolySpawnHurt);
1571 			po->tag = tag;
1572 			po->seqType = sd->linedef->args[2];
1573 			if (po->seqType < 0 || po->seqType > 63)
1574 			{
1575 				po->seqType = 0;
1576 			}
1577 			break;
1578 		}
1579 	}
1580 	if (po->Sidedefs.Size() == 0)
1581 	{
1582 		// didn't find a polyobj through PO_LINE_START
1583 		TArray<side_t *> polySideList;
1584 		unsigned int psIndexOld;
1585 		psIndexOld = po->Sidedefs.Size();
1586 
1587 		for (ii = 0; ii < KnownPolySides.Size(); ++ii)
1588 		{
1589 			i = KnownPolySides[ii];
1590 
1591 			if (i >= 0 &&
1592 				sides[i].linedef->special == Polyobj_ExplicitLine &&
1593 				sides[i].linedef->args[0] == tag)
1594 			{
1595 				if (!sides[i].linedef->args[1])
1596 				{
1597 					I_Error("SpawnPolyobj: Explicit line missing order number in poly %d, linedef %d.\n", tag, int(sides[i].linedef - lines));
1598 				}
1599 				po->Sidedefs.Push (&sides[i]);
1600 			}
1601 		}
1602 		qsort(&po->Sidedefs[0], po->Sidedefs.Size(), sizeof(po->Sidedefs[0]), posicmp);
1603 		if (po->Sidedefs.Size() > 0)
1604 		{
1605 			po->crush = (type != SMT_PolySpawn) ? 3 : 0;
1606 			po->bHurtOnTouch = (type == SMT_PolySpawnHurt);
1607 			po->tag = tag;
1608 			po->seqType = po->Sidedefs[0]->linedef->args[3];
1609 			po->MirrorNum = po->Sidedefs[0]->linedef->args[2];
1610 		}
1611 		else
1612 			I_Error ("SpawnPolyobj: Poly %d does not exist\n", tag);
1613 	}
1614 
1615 	validcount++;
1616 	for(unsigned int i=0; i<po->Sidedefs.Size(); i++)
1617 	{
1618 		line_t *l = po->Sidedefs[i]->linedef;
1619 
1620 		if (l->validcount != validcount)
1621 		{
1622 			l->validcount = validcount;
1623 			po->Linedefs.Push(l);
1624 
1625 			vertex_t *v = l->v1;
1626 			int j;
1627 			for(j = po->Vertices.Size() - 1; j >= 0; j--)
1628 			{
1629 				if (po->Vertices[j] == v) break;
1630 			}
1631 			if (j < 0) po->Vertices.Push(v);
1632 
1633 			v = l->v2;
1634 			for(j = po->Vertices.Size() - 1; j >= 0; j--)
1635 			{
1636 				if (po->Vertices[j] == v) break;
1637 			}
1638 			if (j < 0) po->Vertices.Push(v);
1639 
1640 		}
1641 	}
1642 	po->Sidedefs.ShrinkToFit();
1643 	po->Linedefs.ShrinkToFit();
1644 	po->Vertices.ShrinkToFit();
1645 }
1646 
1647 //==========================================================================
1648 //
1649 // TranslateToStartSpot
1650 //
1651 //==========================================================================
1652 
TranslateToStartSpot(int tag,int originX,int originY)1653 static void TranslateToStartSpot (int tag, int originX, int originY)
1654 {
1655 	FPolyObj *po;
1656 	int deltaX;
1657 	int deltaY;
1658 
1659 	po = NULL;
1660 	for (int i = 0; i < po_NumPolyobjs; i++)
1661 	{
1662 		if (polyobjs[i].tag == tag)
1663 		{
1664 			po = &polyobjs[i];
1665 			break;
1666 		}
1667 	}
1668 	if (po == NULL)
1669 	{ // didn't match the tag with a polyobj tag
1670 		I_Error("TranslateToStartSpot: Unable to match polyobj tag: %d\n", tag);
1671 	}
1672 	if (po->Sidedefs.Size() == 0)
1673 	{
1674 		I_Error ("TranslateToStartSpot: Anchor point located without a StartSpot point: %d\n", tag);
1675 	}
1676 	po->OriginalPts.Resize(po->Sidedefs.Size());
1677 	po->PrevPts.Resize(po->Sidedefs.Size());
1678 	deltaX = originX - po->StartSpot.x;
1679 	deltaY = originY - po->StartSpot.y;
1680 
1681 	for (unsigned i = 0; i < po->Sidedefs.Size(); i++)
1682 	{
1683 		po->Sidedefs[i]->Flags |= WALLF_POLYOBJ;
1684 	}
1685 	for (unsigned i = 0; i < po->Linedefs.Size(); i++)
1686 	{
1687 		po->Linedefs[i]->bbox[BOXTOP] -= deltaY;
1688 		po->Linedefs[i]->bbox[BOXBOTTOM] -= deltaY;
1689 		po->Linedefs[i]->bbox[BOXLEFT] -= deltaX;
1690 		po->Linedefs[i]->bbox[BOXRIGHT] -= deltaX;
1691 	}
1692 	for (unsigned i = 0; i < po->Vertices.Size(); i++)
1693 	{
1694 		po->Vertices[i]->x -= deltaX;
1695 		po->Vertices[i]->y -= deltaY;
1696 		po->OriginalPts[i].x = po->Vertices[i]->x - po->StartSpot.x;
1697 		po->OriginalPts[i].y = po->Vertices[i]->y - po->StartSpot.y;
1698 	}
1699 	po->CalcCenter();
1700 	// For compatibility purposes
1701 	po->CenterSubsector = R_PointInSubsector(po->CenterSpot.x, po->CenterSpot.y);
1702 }
1703 
1704 //==========================================================================
1705 //
1706 // PO_Init
1707 //
1708 //==========================================================================
1709 
PO_Init(void)1710 void PO_Init (void)
1711 {
1712 	// [RH] Hexen found the polyobject-related things by reloading the map's
1713 	//		THINGS lump here and scanning through it. I have P_SpawnMapThing()
1714 	//		record those things instead, so that in here we simply need to
1715 	//		look at the polyspawns list.
1716 	polyspawns_t *polyspawn, **prev;
1717 	int polyIndex;
1718 
1719 	// [RH] Make this faster
1720 	InitSideLists ();
1721 
1722 	polyobjs = new FPolyObj[po_NumPolyobjs];
1723 
1724 	polyIndex = 0; // index polyobj number
1725 	// Find the startSpot points, and spawn each polyobj
1726 	for (polyspawn = polyspawns, prev = &polyspawns; polyspawn;)
1727 	{
1728 		// 9301 (3001) = no crush, 9302 (3002) = crushing, 9303 = hurting touch
1729 		if (polyspawn->type >= SMT_PolySpawn &&	polyspawn->type <= SMT_PolySpawnHurt)
1730 		{
1731 			// Polyobj StartSpot Pt.
1732 			polyobjs[polyIndex].StartSpot.x = polyspawn->x;
1733 			polyobjs[polyIndex].StartSpot.y = polyspawn->y;
1734 			SpawnPolyobj(polyIndex, polyspawn->angle, polyspawn->type);
1735 			polyIndex++;
1736 			*prev = polyspawn->next;
1737 			delete polyspawn;
1738 			polyspawn = *prev;
1739 		}
1740 		else
1741 		{
1742 			prev = &polyspawn->next;
1743 			polyspawn = polyspawn->next;
1744 		}
1745 	}
1746 	for (polyspawn = polyspawns; polyspawn;)
1747 	{
1748 		polyspawns_t *next = polyspawn->next;
1749 		if (polyspawn->type == SMT_PolyAnchor)
1750 		{
1751 			// Polyobj Anchor Pt.
1752 			TranslateToStartSpot (polyspawn->angle, polyspawn->x, polyspawn->y);
1753 		}
1754 		delete polyspawn;
1755 		polyspawn = next;
1756 	}
1757 	polyspawns = NULL;
1758 
1759 	// check for a startspot without an anchor point
1760 	for (polyIndex = 0; polyIndex < po_NumPolyobjs; polyIndex++)
1761 	{
1762 		if (polyobjs[polyIndex].OriginalPts.Size() == 0)
1763 		{
1764 			I_Error ("PO_Init: StartSpot located without an Anchor point: %d\n",
1765 				polyobjs[polyIndex].tag);
1766 		}
1767 	}
1768 	InitBlockMap();
1769 
1770 	// [RH] Don't need the seg lists anymore
1771 	KillSideLists ();
1772 
1773 	for(int i=0;i<numnodes;i++)
1774 	{
1775 		node_t *no = &nodes[i];
1776 		double fdx = (double)no->dx;
1777 		double fdy = (double)no->dy;
1778 		no->len = (float)sqrt(fdx * fdx + fdy * fdy);
1779 	}
1780 
1781 	// mark all subsectors which have a seg belonging to a polyobj
1782 	// These ones should not be rendered on the textured automap.
1783 	for (int i = 0; i < numsubsectors; i++)
1784 	{
1785 		subsector_t *ss = &subsectors[i];
1786 		for(DWORD j=0;j<ss->numlines; j++)
1787 		{
1788 			if (ss->firstline[j].sidedef != NULL &&
1789 				ss->firstline[j].sidedef->Flags & WALLF_POLYOBJ)
1790 			{
1791 				ss->flags |= SSECF_POLYORG;
1792 				break;
1793 			}
1794 		}
1795 	}
1796 
1797 }
1798 
1799 //==========================================================================
1800 //
1801 // PO_Busy
1802 //
1803 //==========================================================================
1804 
PO_Busy(int polyobj)1805 bool PO_Busy (int polyobj)
1806 {
1807 	FPolyObj *poly;
1808 
1809 	poly = PO_GetPolyobj (polyobj);
1810 	return (poly != NULL && poly->specialdata != NULL);
1811 }
1812 
1813 
1814 
1815 //==========================================================================
1816 //
1817 //
1818 //
1819 //==========================================================================
1820 
ClearSubsectorLinks()1821 void FPolyObj::ClearSubsectorLinks()
1822 {
1823 	while (subsectorlinks != NULL)
1824 	{
1825 		assert(subsectorlinks->state == 1337);
1826 
1827 		FPolyNode *next = subsectorlinks->snext;
1828 
1829 		if (subsectorlinks->pnext != NULL)
1830 		{
1831 			assert(subsectorlinks->pnext->state == 1337);
1832 			subsectorlinks->pnext->pprev = subsectorlinks->pprev;
1833 		}
1834 
1835 		if (subsectorlinks->pprev != NULL)
1836 		{
1837 			assert(subsectorlinks->pprev->state == 1337);
1838 			subsectorlinks->pprev->pnext = subsectorlinks->pnext;
1839 		}
1840 		else
1841 		{
1842 			subsectorlinks->subsector->polys = subsectorlinks->pnext;
1843 		}
1844 
1845 		if (subsectorlinks->subsector->BSP != NULL)
1846 		{
1847 			subsectorlinks->subsector->BSP->bDirty = true;
1848 		}
1849 
1850 		subsectorlinks->state = -1;
1851 		delete subsectorlinks;
1852 		subsectorlinks = next;
1853 	}
1854 	subsectorlinks = NULL;
1855 }
1856 
ClearAllSubsectorLinks()1857 void FPolyObj::ClearAllSubsectorLinks()
1858 {
1859 	for (int i = 0; i < po_NumPolyobjs; i++)
1860 	{
1861 		polyobjs[i].ClearSubsectorLinks();
1862 	}
1863 	ReleaseAllPolyNodes();
1864 }
1865 
1866 //==========================================================================
1867 //
1868 // GetIntersection
1869 //
1870 // adapted from P_InterceptVector
1871 //
1872 //==========================================================================
1873 
GetIntersection(FPolySeg * seg,node_t * bsp,FPolyVertex * v)1874 static bool GetIntersection(FPolySeg *seg, node_t *bsp, FPolyVertex *v)
1875 {
1876 	double frac;
1877 	double num;
1878 	double den;
1879 
1880 	double v2x = (double)seg->v1.x;
1881 	double v2y = (double)seg->v1.y;
1882 	double v2dx = (double)(seg->v2.x - seg->v1.x);
1883 	double v2dy = (double)(seg->v2.y - seg->v1.y);
1884 	double v1x = (double)bsp->x;
1885 	double v1y = (double)bsp->y;
1886 	double v1dx = (double)bsp->dx;
1887 	double v1dy = (double)bsp->dy;
1888 
1889 	den = v1dy*v2dx - v1dx*v2dy;
1890 
1891 	if (den == 0)
1892 		return false;		// parallel
1893 
1894 	num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx;
1895 	frac = num / den;
1896 
1897 	if (frac < 0. || frac > 1.) return false;
1898 
1899 	v->x = xs_RoundToInt(v2x + frac * v2dx);
1900 	v->y = xs_RoundToInt(v2y + frac * v2dy);
1901 	return true;
1902 }
1903 
1904 //==========================================================================
1905 //
1906 // PartitionDistance
1907 //
1908 // Determine the distance of a vertex to a node's partition line.
1909 //
1910 //==========================================================================
1911 
PartitionDistance(FPolyVertex * vt,node_t * node)1912 static double PartitionDistance(FPolyVertex *vt, node_t *node)
1913 {
1914 	return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / node->len;
1915 }
1916 
1917 //==========================================================================
1918 //
1919 // AddToBBox
1920 //
1921 //==========================================================================
1922 
AddToBBox(fixed_t child[4],fixed_t parent[4])1923 static void AddToBBox(fixed_t child[4], fixed_t parent[4])
1924 {
1925 	if (child[BOXTOP] > parent[BOXTOP])
1926 	{
1927 		parent[BOXTOP] = child[BOXTOP];
1928 	}
1929 	if (child[BOXBOTTOM] < parent[BOXBOTTOM])
1930 	{
1931 		parent[BOXBOTTOM] = child[BOXBOTTOM];
1932 	}
1933 	if (child[BOXLEFT] < parent[BOXLEFT])
1934 	{
1935 		parent[BOXLEFT] = child[BOXLEFT];
1936 	}
1937 	if (child[BOXRIGHT] > parent[BOXRIGHT])
1938 	{
1939 		parent[BOXRIGHT] = child[BOXRIGHT];
1940 	}
1941 }
1942 
1943 //==========================================================================
1944 //
1945 // AddToBBox
1946 //
1947 //==========================================================================
1948 
AddToBBox(FPolyVertex * v,fixed_t bbox[4])1949 static void AddToBBox(FPolyVertex *v, fixed_t bbox[4])
1950 {
1951 	if (v->x < bbox[BOXLEFT])
1952 	{
1953 		bbox[BOXLEFT] = v->x;
1954 	}
1955 	if (v->x > bbox[BOXRIGHT])
1956 	{
1957 		bbox[BOXRIGHT] = v->x;
1958 	}
1959 	if (v->y < bbox[BOXBOTTOM])
1960 	{
1961 		bbox[BOXBOTTOM] = v->y;
1962 	}
1963 	if (v->y > bbox[BOXTOP])
1964 	{
1965 		bbox[BOXTOP] = v->y;
1966 	}
1967 }
1968 
1969 //==========================================================================
1970 //
1971 // SplitPoly
1972 //
1973 //==========================================================================
1974 
SplitPoly(FPolyNode * pnode,void * node,fixed_t bbox[4])1975 static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4])
1976 {
1977 	static TArray<FPolySeg> lists[2];
1978 	static const double POLY_EPSILON = 0.3125;
1979 
1980 	if (!((size_t)node & 1))  // Keep going until found a subsector
1981 	{
1982 		node_t *bsp = (node_t *)node;
1983 
1984 		int centerside = R_PointOnSide(pnode->poly->CenterSpot.x, pnode->poly->CenterSpot.y, bsp);
1985 
1986 		lists[0].Clear();
1987 		lists[1].Clear();
1988 		for(unsigned i=0;i<pnode->segs.Size(); i++)
1989 		{
1990 			FPolySeg *seg = &pnode->segs[i];
1991 
1992 			// Parts of the following code were taken from Eternity and are
1993 			// being used with permission.
1994 
1995 			// get distance of vertices from partition line
1996 			// If the distance is too small, we may decide to
1997 			// change our idea of sidedness.
1998 			double dist_v1 = PartitionDistance(&seg->v1, bsp);
1999 			double dist_v2 = PartitionDistance(&seg->v2, bsp);
2000 
2001 			// If the distances are less than epsilon, consider the points as being
2002 			// on the same side as the polyobj origin. Why? People like to build
2003 			// polyobject doors flush with their door tracks. This breaks using the
2004 			// usual assumptions.
2005 
2006 
2007 			// Addition to Eternity code: We must also check any seg with only one
2008 			// vertex inside the epsilon threshold. If not, these lines will get split but
2009 			// adjoining ones with both vertices inside the threshold won't thus messing up
2010 			// the order in which they get drawn.
2011 
2012 			if(dist_v1 <= POLY_EPSILON)
2013 			{
2014 				if (dist_v2 <= POLY_EPSILON)
2015 				{
2016 					lists[centerside].Push(*seg);
2017 				}
2018 				else
2019 				{
2020 					int side = R_PointOnSide(seg->v2.x, seg->v2.y, bsp);
2021 					lists[side].Push(*seg);
2022 				}
2023 			}
2024 			else if (dist_v2 <= POLY_EPSILON)
2025 			{
2026 				int side = R_PointOnSide(seg->v1.x, seg->v1.y, bsp);
2027 				lists[side].Push(*seg);
2028 			}
2029 			else
2030 			{
2031 				int side1 = R_PointOnSide(seg->v1.x, seg->v1.y, bsp);
2032 				int side2 = R_PointOnSide(seg->v2.x, seg->v2.y, bsp);
2033 
2034 				if(side1 != side2)
2035 				{
2036 					// if the partition line crosses this seg, we must split it.
2037 
2038 					FPolyVertex vert;
2039 
2040 					if (GetIntersection(seg, bsp, &vert))
2041 					{
2042 						lists[0].Push(*seg);
2043 						lists[1].Push(*seg);
2044 						lists[side1].Last().v2 = vert;
2045 						lists[side2].Last().v1 = vert;
2046 					}
2047 					else
2048 					{
2049 						// should never happen
2050 						lists[side1].Push(*seg);
2051 					}
2052 				}
2053 				else
2054 				{
2055 					// both points on the same side.
2056 					lists[side1].Push(*seg);
2057 				}
2058 			}
2059 		}
2060 		if (lists[1].Size() == 0)
2061 		{
2062 			SplitPoly(pnode, bsp->children[0], bsp->bbox[0]);
2063 			AddToBBox(bsp->bbox[0], bbox);
2064 		}
2065 		else if (lists[0].Size() == 0)
2066 		{
2067 			SplitPoly(pnode, bsp->children[1], bsp->bbox[1]);
2068 			AddToBBox(bsp->bbox[1], bbox);
2069 		}
2070 		else
2071 		{
2072 			// create the new node
2073 			FPolyNode *newnode = NewPolyNode();
2074 			newnode->poly = pnode->poly;
2075 			newnode->segs = lists[1];
2076 
2077 			// set segs for original node
2078 			pnode->segs = lists[0];
2079 
2080 			// recurse back side
2081 			SplitPoly(newnode, bsp->children[1], bsp->bbox[1]);
2082 
2083 			// recurse front side
2084 			SplitPoly(pnode, bsp->children[0], bsp->bbox[0]);
2085 
2086 			AddToBBox(bsp->bbox[0], bbox);
2087 			AddToBBox(bsp->bbox[1], bbox);
2088 		}
2089 	}
2090 	else
2091 	{
2092 		// we reached a subsector so we can link the node with this subsector
2093 		subsector_t *sub = (subsector_t *)((BYTE *)node - 1);
2094 
2095 		// Link node to subsector
2096 		pnode->pnext = sub->polys;
2097 		if (pnode->pnext != NULL)
2098 		{
2099 			assert(pnode->pnext->state == 1337);
2100 			pnode->pnext->pprev = pnode;
2101 		}
2102 		pnode->pprev = NULL;
2103 		sub->polys = pnode;
2104 
2105 		// link node to polyobject
2106 		pnode->snext = pnode->poly->subsectorlinks;
2107 		pnode->poly->subsectorlinks = pnode;
2108 		pnode->subsector = sub;
2109 
2110 		// calculate bounding box for this polynode
2111 		assert(pnode->segs.Size() != 0);
2112 		fixed_t subbbox[4] = { FIXED_MIN, FIXED_MAX, FIXED_MAX, FIXED_MIN };
2113 
2114 		for (unsigned i = 0; i < pnode->segs.Size(); ++i)
2115 		{
2116 			AddToBBox(&pnode->segs[i].v1, subbbox);
2117 			AddToBBox(&pnode->segs[i].v2, subbbox);
2118 		}
2119 		// Potentially expand the parent node's bounding box to contain these bits of polyobject.
2120 		AddToBBox(subbbox, bbox);
2121 	}
2122 }
2123 
2124 //==========================================================================
2125 //
2126 //
2127 //
2128 //==========================================================================
2129 
CreateSubsectorLinks()2130 void FPolyObj::CreateSubsectorLinks()
2131 {
2132 	FPolyNode *node = NewPolyNode();
2133 	// Even though we don't care about it, we need to initialize this
2134 	// bounding box to something so that Valgrind won't complain about it
2135 	// when SplitPoly modifies it.
2136 	fixed_t dummybbox[4] = { 0 };
2137 
2138 	node->poly = this;
2139 	node->segs.Resize(Sidedefs.Size());
2140 
2141 	for(unsigned i=0; i<Sidedefs.Size(); i++)
2142 	{
2143 		FPolySeg *seg = &node->segs[i];
2144 		side_t *side = Sidedefs[i];
2145 
2146 		seg->v1 = side->V1();
2147 		seg->v2 = side->V2();
2148 		seg->wall = side;
2149 	}
2150 	if (!(i_compatflags & COMPATF_POLYOBJ))
2151 	{
2152 		SplitPoly(node, nodes + numnodes - 1, dummybbox);
2153 	}
2154 	else
2155 	{
2156 		subsector_t *sub = CenterSubsector;
2157 
2158 		// Link node to subsector
2159 		node->pnext = sub->polys;
2160 		if (node->pnext != NULL)
2161 		{
2162 			assert(node->pnext->state == 1337);
2163 			node->pnext->pprev = node;
2164 		}
2165 		node->pprev = NULL;
2166 		sub->polys = node;
2167 
2168 		// link node to polyobject
2169 		node->snext = node->poly->subsectorlinks;
2170 		node->poly->subsectorlinks = node;
2171 		node->subsector = sub;
2172 	}
2173 }
2174 
2175 //==========================================================================
2176 //
2177 //
2178 //
2179 //==========================================================================
2180 
PO_LinkToSubsectors()2181 void PO_LinkToSubsectors()
2182 {
2183 	for (int i = 0; i < po_NumPolyobjs; i++)
2184 	{
2185 		if (polyobjs[i].subsectorlinks == NULL)
2186 		{
2187 			polyobjs[i].CreateSubsectorLinks();
2188 		}
2189 	}
2190 }
2191 
2192 //==========================================================================
2193 //
2194 // NewPolyNode
2195 //
2196 //==========================================================================
2197 
NewPolyNode()2198 static FPolyNode *NewPolyNode()
2199 {
2200 	FPolyNode *node;
2201 
2202 	if (FreePolyNodes != NULL)
2203 	{
2204 		node = FreePolyNodes;
2205 		FreePolyNodes = node->pnext;
2206 	}
2207 	else
2208 	{
2209 		node = new FPolyNode;
2210 	}
2211 	node->state = 1337;
2212 	node->poly = NULL;
2213 	node->pnext = NULL;
2214 	node->pprev = NULL;
2215 	node->subsector = NULL;
2216 	node->snext = NULL;
2217 	return node;
2218 }
2219 
2220 //==========================================================================
2221 //
2222 // FreePolyNode
2223 //
2224 //==========================================================================
2225 
FreePolyNode(FPolyNode * node)2226 void FreePolyNode(FPolyNode *node)
2227 {
2228 	node->segs.Clear();
2229 	node->pnext = FreePolyNodes;
2230 	FreePolyNodes = node;
2231 }
2232 
2233 //==========================================================================
2234 //
2235 // ReleaseAllPolyNodes
2236 //
2237 //==========================================================================
2238 
ReleaseAllPolyNodes()2239 void ReleaseAllPolyNodes()
2240 {
2241 	FPolyNode *node, *next;
2242 
2243 	for (node = FreePolyNodes; node != NULL; node = next)
2244 	{
2245 		next = node->pnext;
2246 		delete node;
2247 	}
2248 }
2249 
2250 //==========================================================================
2251 //
2252 // FPolyMirrorIterator Constructor
2253 //
2254 // This class is used to avoid infinitely looping on cyclical chains of
2255 // mirrored polyobjects.
2256 //
2257 //==========================================================================
2258 
FPolyMirrorIterator(FPolyObj * poly)2259 FPolyMirrorIterator::FPolyMirrorIterator(FPolyObj *poly)
2260 {
2261 	CurPoly = poly;
2262 	if (poly != NULL)
2263 	{
2264 		UsedPolys[0] = poly->tag;
2265 		NumUsedPolys = 1;
2266 	}
2267 	else
2268 	{
2269 		NumUsedPolys = 0;
2270 	}
2271 }
2272 
2273 //==========================================================================
2274 //
2275 // FPolyMirrorIterator :: NextMirror
2276 //
2277 // Returns the polyobject that mirrors the current one, or NULL if there
2278 // is no mirroring polyobject, or there is a mirroring polyobject but it was
2279 // already returned.
2280 //
2281 //==========================================================================
2282 
NextMirror()2283 FPolyObj *FPolyMirrorIterator::NextMirror()
2284 {
2285 	FPolyObj *poly = CurPoly, *nextpoly;
2286 
2287 	if (poly == NULL)
2288 	{
2289 		return NULL;
2290 	}
2291 
2292 	// Do the work to decide which polyobject to return the next time this
2293 	// function is called.
2294 	int mirror = poly->GetMirror(), i;
2295 	nextpoly = NULL;
2296 
2297 	// Is there a mirror and we have room to remember it?
2298 	if (mirror != 0 && NumUsedPolys != countof(UsedPolys))
2299 	{
2300 		// Has this polyobject been returned already?
2301 		for (i = 0; i < NumUsedPolys; ++i)
2302 		{
2303 			if (UsedPolys[i] == mirror)
2304 			{
2305 				break;	// Yes, it has been returned.
2306 			}
2307 		}
2308 		if (i == NumUsedPolys)
2309 		{ // No, it has not been returned.
2310 			UsedPolys[NumUsedPolys++] = mirror;
2311 			nextpoly = PO_GetPolyobj(mirror);
2312 			if (nextpoly == NULL)
2313 			{
2314 				Printf("Invalid mirror polyobj num %d for polyobj num %d\n", mirror, UsedPolys[i - 1]);
2315 			}
2316 		}
2317 	}
2318 	CurPoly = nextpoly;
2319 	return poly;
2320 }
2321