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