From d4f87203bd6da64d21f2fba68502e2524492d215 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Feb 2016 23:08:32 +0100 Subject: [PATCH] - added portal awareness to several functions. * Arch-Vile resurrection * Boom point pushers (due to complete lack of z-handling only for line portals.) * A_RadiusGive These also require a more thorough collection of portal groups than simple position checks. --- src/g_hexen/a_spike.cpp | 1 + src/p_enemy.cpp | 50 ++++---- src/p_maputl.h | 12 +- src/p_spec.cpp | 9 +- src/portal.cpp | 196 ++++++++++++++++++++---------- src/thingdef/thingdef_codeptr.cpp | 10 +- 6 files changed, 187 insertions(+), 91 deletions(-) diff --git a/src/g_hexen/a_spike.cpp b/src/g_hexen/a_spike.cpp index f1538d3d9..4af4e2d54 100644 --- a/src/g_hexen/a_spike.cpp +++ b/src/g_hexen/a_spike.cpp @@ -154,6 +154,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ThrustImpale) PARAM_ACTION_PROLOGUE; AActor *thing; + // This doesn't need to iterate through portals. FBlockThingsIterator it(FBoundingBox(self->X(), self->Y(), self->radius)); while ((thing = it.Next())) { diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 576d0c4fa..0e8c7d6ff 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -2604,40 +2604,48 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates) fixedvec2 viletry = self->Vec2Offset( FixedMul (absSpeed, xspeed[self->movedir]), FixedMul (absSpeed, yspeed[self->movedir]), true); - AActor *corpsehit; - FBlockThingsIterator it(FBoundingBox(viletry.x, viletry.y, 32*FRACUNIT)); - while ((corpsehit = it.Next())) + FPortalGroupArray check(FPortalGroupArray::PGA_Full3d); + + FMultiBlockThingsIterator it(check, viletry.x, viletry.y, self->Z() - 64* FRACUNIT, self->Top() + 64 * FRACUNIT, 32 * FRACUNIT); + FMultiBlockThingsIterator::CheckResult cres; + while (it.Next(&cres)) { + AActor *corpsehit = cres.thing; FState *raisestate = corpsehit->GetRaiseState(); if (raisestate != NULL) { // use the current actor's radius instead of the Arch Vile's default. fixed_t maxdist = corpsehit->GetDefault()->radius + self->radius; - if (abs(corpsehit->X() - viletry.x) > maxdist || - abs(corpsehit->Y() - viletry.y) > maxdist) + if (abs(cres.position.x - viletry.x) > maxdist || + abs(cres.position.y - viletry.y) > maxdist) continue; // not actually touching // Let's check if there are floors in between the archvile and its target - // if in a different section of the map, only consider possible if a line of sight exists. - if (corpsehit->Sector->PortalGroup != self->Sector->PortalGroup && !P_CheckSight(self, corpsehit)) - continue; - - sector_t *vilesec = self->Sector; - sector_t *corpsec = corpsehit->Sector; - // We only need to test if at least one of the sectors has a 3D floor. - sector_t *testsec = vilesec->e->XFloor.ffloors.Size() ? vilesec : - (vilesec != corpsec && corpsec->e->XFloor.ffloors.Size()) ? corpsec : NULL; - if (testsec) + if (corpsehit->Sector->PortalGroup != self->Sector->PortalGroup) { - fixed_t zdist1, zdist2; - if (P_Find3DFloor(testsec, corpsehit->Pos(), false, true, zdist1) - != P_Find3DFloor(testsec, self->Pos(), false, true, zdist2)) + // if in a different section of the map, only consider possible if a line of sight exists. + if (!P_CheckSight(self, corpsehit)) + continue; + } + else + { + sector_t *vilesec = self->Sector; + sector_t *corpsec = corpsehit->Sector; + // We only need to test if at least one of the sectors has a 3D floor. + sector_t *testsec = vilesec->e->XFloor.ffloors.Size() ? vilesec : + (vilesec != corpsec && corpsec->e->XFloor.ffloors.Size()) ? corpsec : NULL; + if (testsec) { - // Not on same floor - if (vilesec == corpsec || abs(zdist1 - self->Z()) > self->height) - continue; + fixed_t zdist1, zdist2; + if (P_Find3DFloor(testsec, corpsehit->Pos(), false, true, zdist1) + != P_Find3DFloor(testsec, self->Pos(), false, true, zdist2)) + { + // Not on same floor + if (vilesec == corpsec || abs(zdist1 - self->Z()) > self->height) + continue; + } } } diff --git a/src/p_maputl.h b/src/p_maputl.h index 838c4e775..21d69e1c8 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -129,6 +129,14 @@ struct polyblock_t; struct FPortalGroupArray { + // Controls how groups are connected + enum + { + PGA_NoSectorPortals,// only collect line portals + PGA_CheckPosition, // only collects sector portals at the actual position + PGA_Full3d, // Goes up and down sector portals at any linedef within the bounding box (this is a lot slower and should only be done if really needed.) + }; + enum { LOWER = 0x4000, @@ -141,8 +149,9 @@ struct FPortalGroupArray MAX_STATIC = 4 }; - FPortalGroupArray() + FPortalGroupArray(int collectionmethod = PGA_CheckPosition) { + method = collectionmethod; varused = 0; inited = false; } @@ -171,6 +180,7 @@ struct FPortalGroupArray } bool inited; + int method; private: WORD entry[MAX_STATIC]; diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 23bd2f911..e229b973f 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -2249,11 +2249,14 @@ void DPusher::Tick () // Seek out all pushable things within the force radius of this // point pusher. Crosses sectors, so use blockmap. - FBlockThingsIterator it(FBoundingBox(m_X, m_Y, m_Radius)); - AActor *thing; + FPortalGroupArray check(FPortalGroupArray::PGA_NoSectorPortals); // no sector portals because this thing is utterly z-unaware. + FMultiBlockThingsIterator it(check, m_X, m_Y, 0, 0, m_Radius); + FMultiBlockThingsIterator::CheckResult cres; - while ((thing = it.Next())) + + while (it.Next(&cres)) { + AActor *thing = cres.thing; // Normal ZDoom is based only on the WINDTHRUST flag, with the noclip cheat as an exemption. bool pusharound = ((thing->flags2 & MF2_WINDTHRUST) && !(thing->flags & MF_NOCLIP)); diff --git a/src/portal.cpp b/src/portal.cpp index addb92968..92d753407 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -1117,87 +1117,157 @@ void P_CreateLinkedPortals() // //============================================================================ + +static bool ProcessLayer() +{ +} + bool P_CollectConnectedGroups(int startgroup, const fixedvec3 &position, fixed_t upperz, fixed_t checkradius, FPortalGroupArray &out) { // Keep this temporary work stuff static. This function can never be called recursively // and this would have to be reallocated for each call otherwise. static FPortalBits processMask; static TArray foundPortals; + static TArray groupsToCheck; bool retval = false; out.inited = true; - if (linkedPortals.Size() == 0) + if (linkedPortals.Size() != 0) { - // If there are no portals, all sectors are in group 0. - return false; - } - processMask.setSize(linkedPortals.Size()); - processMask.clear(); - foundPortals.Clear(); + processMask.setSize(linkedPortals.Size()); + processMask.clear(); + foundPortals.Clear(); - int thisgroup = startgroup; - processMask.setBit(thisgroup); - //out.Add(thisgroup); + int thisgroup = startgroup; + processMask.setBit(thisgroup); + //out.Add(thisgroup); - for (unsigned i = 0; i < linkedPortals.Size(); i++) - { - line_t *ld = linkedPortals[i]->mOrigin; - int othergroup = ld->frontsector->PortalGroup; - FDisplacement &disp = Displacements(thisgroup, othergroup); - if (!disp.isSet) continue; // no connection. - - FBoundingBox box(position.x + disp.pos.x, position.y + disp.pos.y, checkradius); - - if (box.Right() <= ld->bbox[BOXLEFT] - || box.Left() >= ld->bbox[BOXRIGHT] - || box.Top() <= ld->bbox[BOXBOTTOM] - || box.Bottom() >= ld->bbox[BOXTOP]) - continue; // not touched - - if (box.BoxOnLineSide(linkedPortals[i]->mOrigin) != -1) continue; // not touched - foundPortals.Push(linkedPortals[i]); - } - bool foundone = true; - while (foundone) - { - foundone = false; - for (int i = foundPortals.Size() - 1; i >= 0; i--) + for (unsigned i = 0; i < linkedPortals.Size(); i++) { - if (processMask.getBit(foundPortals[i]->mOrigin->frontsector->PortalGroup) && - !processMask.getBit(foundPortals[i]->mDestination->frontsector->PortalGroup)) + line_t *ld = linkedPortals[i]->mOrigin; + int othergroup = ld->frontsector->PortalGroup; + FDisplacement &disp = Displacements(thisgroup, othergroup); + if (!disp.isSet) continue; // no connection. + + FBoundingBox box(position.x + disp.pos.x, position.y + disp.pos.y, checkradius); + + if (box.Right() <= ld->bbox[BOXLEFT] + || box.Left() >= ld->bbox[BOXRIGHT] + || box.Top() <= ld->bbox[BOXBOTTOM] + || box.Bottom() >= ld->bbox[BOXTOP]) + continue; // not touched + + if (box.BoxOnLineSide(linkedPortals[i]->mOrigin) != -1) continue; // not touched + foundPortals.Push(linkedPortals[i]); + } + bool foundone = true; + while (foundone) + { + foundone = false; + for (int i = foundPortals.Size() - 1; i >= 0; i--) { - processMask.setBit(foundPortals[i]->mDestination->frontsector->PortalGroup); - out.Add(foundPortals[i]->mDestination->frontsector->PortalGroup); - foundone = true; - retval = true; - foundPortals.Delete(i); + if (processMask.getBit(foundPortals[i]->mOrigin->frontsector->PortalGroup) && + !processMask.getBit(foundPortals[i]->mDestination->frontsector->PortalGroup)) + { + processMask.setBit(foundPortals[i]->mDestination->frontsector->PortalGroup); + out.Add(foundPortals[i]->mDestination->frontsector->PortalGroup); + foundone = true; + retval = true; + foundPortals.Delete(i); + } } } } - sector_t *sec = P_PointInSector(position.x, position.y); - sector_t *wsec = sec; - while (!wsec->PortalBlocksMovement(sector_t::ceiling) && upperz > wsec->SkyBoxes[sector_t::ceiling]->threshold) + if (out.method != FPortalGroupArray::PGA_NoSectorPortals) { - sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; - fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup); - fixed_t dx = position.x + pos.x; - fixed_t dy = position.y + pos.y; - processMask.setBit(othersec->PortalGroup); - out.Add(othersec->PortalGroup|FPortalGroupArray::UPPER); - wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat - retval = true; - } - wsec = sec; - while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold) - { - sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector; - fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup); - fixed_t dx = position.x + pos.x; - fixed_t dy = position.y + pos.y; - processMask.setBit(othersec->PortalGroup|FPortalGroupArray::LOWER); - out.Add(othersec->PortalGroup); - wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat - retval = true; + sector_t *sec = P_PointInSector(position.x, position.y); + sector_t *wsec = sec; + while (!wsec->PortalBlocksMovement(sector_t::ceiling) && upperz > wsec->SkyBoxes[sector_t::ceiling]->threshold) + { + sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; + fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup); + fixed_t dx = position.x + pos.x; + fixed_t dy = position.y + pos.y; + processMask.setBit(othersec->PortalGroup); + out.Add(othersec->PortalGroup | FPortalGroupArray::UPPER); + wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat + retval = true; + } + wsec = sec; + while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold) + { + sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector; + fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup); + fixed_t dx = position.x + pos.x; + fixed_t dy = position.y + pos.y; + processMask.setBit(othersec->PortalGroup | FPortalGroupArray::LOWER); + out.Add(othersec->PortalGroup); + wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat + retval = true; + } + if (out.method == FPortalGroupArray::PGA_Full3d) + { + groupsToCheck.Clear(); + groupsToCheck.Push(startgroup); + int thisgroup = startgroup; + for (unsigned i = 0; i < groupsToCheck.Size();i++) + { + fixedvec2 disp = Displacements.getOffset(startgroup, thisgroup & ~FPortalGroupArray::FLAT); + FBoundingBox box(position.x + disp.x, position.y + disp.y, checkradius); + FBlockLinesIterator it(box); + line_t *ld; + while ((ld = it.Next())) + { + if (box.Right() <= ld->bbox[BOXLEFT] + || box.Left() >= ld->bbox[BOXRIGHT] + || box.Top() <= ld->bbox[BOXBOTTOM] + || box.Bottom() >= ld->bbox[BOXTOP]) + continue; + + if (box.BoxOnLineSide(ld) != -1) + continue; + + if (!(thisgroup & FPortalGroupArray::LOWER)) + { + for (int s = 0; s < 2; s++) + { + sector_t *sec = s ? ld->backsector : ld->frontsector; + if (sec && !(sec->PortalBlocksMovement(sector_t::ceiling))) + { + if (sec->SkyBoxes[sector_t::ceiling]->threshold < upperz) + { + int grp = sec->SkyBoxes[sector_t::ceiling]->Sector->PortalGroup; + if (!(processMask.getBit(grp))) + { + processMask.setBit(grp); + groupsToCheck.Push(grp | FPortalGroupArray::UPPER); + } + } + } + } + } + if (!(thisgroup & FPortalGroupArray::UPPER)) + { + for (int s = 0; s < 2; s++) + { + sector_t *sec = s ? ld->backsector : ld->frontsector; + if (sec && !(sec->PortalBlocksMovement(sector_t::floor))) + { + if (sec->SkyBoxes[sector_t::floor]->threshold > position.z) + { + int grp = sec->SkyBoxes[sector_t::floor]->Sector->PortalGroup; + if (!(processMask.getBit(grp))) + { + processMask.setBit(grp); + groupsToCheck.Push(grp | FPortalGroupArray::LOWER); + } + } + } + } + } + } + } + } } return retval; } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index ec876da15..19c34cd4f 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -5570,10 +5570,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive) } else { - FBlockThingsIterator it(FBoundingBox(self->X(), self->Y(), distance)); - while ((thing = it.Next())) + FPortalGroupArray check(FPortalGroupArray::PGA_Full3d); + fixed_t mid = self->Z() + self->height / 2; + FMultiBlockThingsIterator it(check, self->X(), self->Y(), mid-distance, mid+distance, distance); + FMultiBlockThingsIterator::CheckResult cres; + + while ((it.Next(&cres))) { - given += DoRadiusGive(self, thing, item, amount, distance, flags, filter, species, mindist); + given += DoRadiusGive(self, cres.thing, item, amount, distance, flags, filter, species, mindist); } } ACTION_RETURN_INT(given);