diff --git a/src/p_map.cpp b/src/p_map.cpp index c372fa3ef4..5ce9b837a8 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -334,7 +334,7 @@ void P_FindFloorCeiling(AActor *actor, int flags) validcount++; FPortalGroupArray grouplist; - FMultiBlockLinesIterator mit(grouplist, actor, tmf.x, tmf.y, actor->radius); + FMultiBlockLinesIterator mit(grouplist, actor); FMultiBlockLinesIterator::CheckResult cres; // if we already have a valid floor/ceiling sector within the current sector, @@ -420,7 +420,7 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra thing->SetZ(z); FPortalGroupArray grouplist; - FMultiBlockLinesIterator mit(grouplist, thing, tmf.x, tmf.y, thing->radius); + FMultiBlockLinesIterator mit(grouplist, x, y, z, thing->height, thing->radius); FMultiBlockLinesIterator::CheckResult cres; while (mit.Next(&cres)) @@ -431,11 +431,13 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra if (tmf.touchmidtex) tmf.dropoffz = tmf.floorz; - FBlockThingsIterator it2(FBoundingBox(x, y, thing->radius)); - AActor *th; + FMultiBlockThingsIterator mit2(grouplist, x, y, z, thing->height, thing->radius); + FMultiBlockThingsIterator::CheckResult cres2; - while ((th = it2.Next())) + while (mit2.Next(&cres2)) { + AActor *th = cres2.thing; + if (!(th->flags & MF_SHOOTABLE)) continue; @@ -444,7 +446,7 @@ bool P_TeleportMove(AActor *thing, fixed_t x, fixed_t y, fixed_t z, bool telefra continue; fixed_t blockdist = th->radius + tmf.thing->radius; - if (abs(th->X() - tmf.x) >= blockdist || abs(th->Y() - tmf.y) >= blockdist) + if (abs(th->X() - cres.position.x) >= blockdist || abs(th->Y() - cres.position.y) >= blockdist) continue; if ((th->flags2 | tmf.thing->flags2) & MF2_THRUACTORS) diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index e5396e484d..59a7b9b546 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -711,18 +711,34 @@ line_t *FBlockLinesIterator::Next() // //=========================================================================== -FMultiBlockLinesIterator::FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkx, fixed_t checky, fixed_t checkradius) +FMultiBlockLinesIterator::FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius) : checklist(check) { checkpoint = origin->Pos(); - if (checkx != FIXED_MAX) checkpoint.x = checkx; - if (checky != FIXED_MAX) checkpoint.y = checky; - P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist); + if (!check.inited) P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist); checkpoint.z = checkradius; basegroup = origin->Sector->PortalGroup; Reset(); } +FMultiBlockLinesIterator::FMultiBlockLinesIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius) + : checklist(check) +{ + checkpoint.x = checkx; + checkpoint.y = checky; + checkpoint.z = checkz; + basegroup = P_PointInSector(checkx, checky)->PortalGroup; + if (!check.inited) P_CollectConnectedGroups(basegroup, checkpoint, checkz + checkh, checkradius, checklist); + checkpoint.z = checkradius; + Reset(); +} + +//=========================================================================== +// +// Go up a ceiling portal +// +//=========================================================================== + bool FMultiBlockLinesIterator::GoUp(fixed_t x, fixed_t y) { if (continueup) @@ -739,6 +755,12 @@ bool FMultiBlockLinesIterator::GoUp(fixed_t x, fixed_t y) return false; } +//=========================================================================== +// +// Go down a floor portal +// +//=========================================================================== + bool FMultiBlockLinesIterator::GoDown(fixed_t x, fixed_t y) { if (continuedown) @@ -755,6 +777,12 @@ bool FMultiBlockLinesIterator::GoDown(fixed_t x, fixed_t y) return false; } +//=========================================================================== +// +// Gets the next line - also manages switching between portal groups +// +//=========================================================================== + bool FMultiBlockLinesIterator::Next(FMultiBlockLinesIterator::CheckResult *item) { line_t *line = blockIterator.Next(); @@ -808,6 +836,12 @@ bool FMultiBlockLinesIterator::Next(FMultiBlockLinesIterator::CheckResult *item) return Next(item); } +//=========================================================================== +// +// start iterating a new group +// +//=========================================================================== + void FMultiBlockLinesIterator::startIteratorForGroup(int group) { offset = Displacements(basegroup, group); @@ -817,6 +851,12 @@ void FMultiBlockLinesIterator::startIteratorForGroup(int group) blockIterator.init(bbox); } +//=========================================================================== +// +// Resets the iterator +// +//=========================================================================== + void FMultiBlockLinesIterator::Reset() { continueup = continueup = true; @@ -851,8 +891,7 @@ FBlockThingsIterator::FBlockThingsIterator(int _minx, int _miny, int _maxx, int Reset(); } -FBlockThingsIterator::FBlockThingsIterator(const FBoundingBox &box) -: DynHash(0) +void FBlockThingsIterator::init(const FBoundingBox &box) { maxy = GetSafeBlockY(box.Top() - bmaporgy); miny = GetSafeBlockY(box.Bottom() - bmaporgy); @@ -994,6 +1033,108 @@ AActor *FBlockThingsIterator::Next(bool centeronly) } + +//=========================================================================== +// +// FMultiBlockThingsIterator :: FMultiBlockThingsIterator +// +// An iterator that can check multiple portal groups. +// +//=========================================================================== + +FMultiBlockThingsIterator::FMultiBlockThingsIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius) + : checklist(check) +{ + checkpoint = origin->Pos(); + if (!check.inited) P_CollectConnectedGroups(origin->Sector->PortalGroup, checkpoint, origin->Top(), checkradius, checklist); + checkpoint.z = checkradius; + basegroup = origin->Sector->PortalGroup; + Reset(); +} + +FMultiBlockThingsIterator::FMultiBlockThingsIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius) + : checklist(check) +{ + checkpoint.x = checkx; + checkpoint.y = checky; + checkpoint.z = checkz; + basegroup = P_PointInSector(checkx, checky)->PortalGroup; + if (!check.inited) P_CollectConnectedGroups(basegroup, checkpoint, checkz + checkh, checkradius, checklist); + checkpoint.z = checkradius; + Reset(); +} + +//=========================================================================== +// +// Gets the next line - also manages switching between portal groups +// +//=========================================================================== + +bool FMultiBlockThingsIterator::Next(FMultiBlockThingsIterator::CheckResult *item) +{ + AActor *thing = blockIterator.Next(); + if (thing != NULL) + { + item->thing = thing; + item->position = checkpoint + Displacements(basegroup, thing->Sector->PortalGroup); + item->portalflags = portalflags; + return true; + } + bool onlast = unsigned(index + 1) >= checklist.Size(); + int nextflags = onlast ? 0 : checklist[index + 1] & FPortalGroupArray::FLAT; + + if (onlast) + { + return false; + } + + index++; + startIteratorForGroup(checklist[index] & ~FPortalGroupArray::FLAT); + switch (nextflags) + { + case FPortalGroupArray::UPPER: + portalflags = FFCF_NOFLOOR; + break; + + case FPortalGroupArray::LOWER: + portalflags = FFCF_NOCEILING; + break; + + default: + portalflags = 0; + } + + return Next(item); +} + +//=========================================================================== +// +// start iterating a new group +// +//=========================================================================== + +void FMultiBlockThingsIterator::startIteratorForGroup(int group) +{ + fixedvec2 offset = Displacements(basegroup, group); + offset.x += checkpoint.x; + offset.y += checkpoint.y; + bbox.setBox(offset.x, offset.y, checkpoint.z); + blockIterator.init(bbox); +} + +//=========================================================================== +// +// Resets the iterator +// +//=========================================================================== + +void FMultiBlockThingsIterator::Reset() +{ + index = -1; + portalflags = 0; + startIteratorForGroup(basegroup); +} + //=========================================================================== // // FPathTraverse :: Intercepts diff --git a/src/p_maputl.h b/src/p_maputl.h index a939855bf3..2aacf3dd1f 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -224,7 +224,8 @@ public: int portalflags; }; - FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkx = FIXED_MAX, fixed_t checky = FIXED_MAX, fixed_t checkradius = -1); + FMultiBlockLinesIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius = -1); + FMultiBlockLinesIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius); bool Next(CheckResult *item); void Reset(); // for stopping group traversal through portals. Only the calling code can decide whether this is needed so this needs to be set from the outside. @@ -274,14 +275,52 @@ class FBlockThingsIterator FBlockThingsIterator(); friend class FPathTraverse; + friend class FMultiBlockThingsIterator; public: FBlockThingsIterator(int minx, int miny, int maxx, int maxy); - FBlockThingsIterator(const FBoundingBox &box); + FBlockThingsIterator(const FBoundingBox &box) + { + init(box); + } + void init(const FBoundingBox &box); AActor *Next(bool centeronly = false); void Reset() { StartBlock(minx, miny); } }; +class FMultiBlockThingsIterator +{ + FPortalGroupArray &checklist; + fixedvec3 checkpoint; + short basegroup; + short portalflags; + short index; + FBlockThingsIterator blockIterator; + FBoundingBox bbox; + + void startIteratorForGroup(int group); + +public: + + struct CheckResult + { + AActor *thing; + fixedvec3 position; + int portalflags; + }; + + FMultiBlockThingsIterator(FPortalGroupArray &check, AActor *origin, fixed_t checkradius = -1); + FMultiBlockThingsIterator(FPortalGroupArray &check, fixed_t checkx, fixed_t checky, fixed_t checkz, fixed_t checkh, fixed_t checkradius); + bool Next(CheckResult *item); + void Reset(); + const FBoundingBox &Box() const + { + return bbox; + } +}; + + + class FPathTraverse { static TArray intercepts;