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