- 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.
This commit is contained in:
Christoph Oelckers 2016-02-28 23:08:32 +01:00
parent 8be690fbf2
commit d4f87203bd
6 changed files with 187 additions and 91 deletions

View file

@ -154,6 +154,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ThrustImpale)
PARAM_ACTION_PROLOGUE; PARAM_ACTION_PROLOGUE;
AActor *thing; AActor *thing;
// This doesn't need to iterate through portals.
FBlockThingsIterator it(FBoundingBox(self->X(), self->Y(), self->radius)); FBlockThingsIterator it(FBoundingBox(self->X(), self->Y(), self->radius));
while ((thing = it.Next())) while ((thing = it.Next()))
{ {

View file

@ -2604,40 +2604,48 @@ static bool P_CheckForResurrection(AActor *self, bool usevilestates)
fixedvec2 viletry = self->Vec2Offset( fixedvec2 viletry = self->Vec2Offset(
FixedMul (absSpeed, xspeed[self->movedir]), FixedMul (absSpeed, xspeed[self->movedir]),
FixedMul (absSpeed, yspeed[self->movedir]), true); FixedMul (absSpeed, yspeed[self->movedir]), true);
AActor *corpsehit;
FBlockThingsIterator it(FBoundingBox(viletry.x, viletry.y, 32*FRACUNIT)); FPortalGroupArray check(FPortalGroupArray::PGA_Full3d);
while ((corpsehit = it.Next()))
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(); FState *raisestate = corpsehit->GetRaiseState();
if (raisestate != NULL) if (raisestate != NULL)
{ {
// use the current actor's radius instead of the Arch Vile's default. // use the current actor's radius instead of the Arch Vile's default.
fixed_t maxdist = corpsehit->GetDefault()->radius + self->radius; fixed_t maxdist = corpsehit->GetDefault()->radius + self->radius;
if (abs(corpsehit->X() - viletry.x) > maxdist || if (abs(cres.position.x - viletry.x) > maxdist ||
abs(corpsehit->Y() - viletry.y) > maxdist) abs(cres.position.y - viletry.y) > maxdist)
continue; // not actually touching continue; // not actually touching
// Let's check if there are floors in between the archvile and its target // 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)
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)
{ {
fixed_t zdist1, zdist2; // if in a different section of the map, only consider possible if a line of sight exists.
if (P_Find3DFloor(testsec, corpsehit->Pos(), false, true, zdist1) if (!P_CheckSight(self, corpsehit))
!= P_Find3DFloor(testsec, self->Pos(), false, true, zdist2)) 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 fixed_t zdist1, zdist2;
if (vilesec == corpsec || abs(zdist1 - self->Z()) > self->height) if (P_Find3DFloor(testsec, corpsehit->Pos(), false, true, zdist1)
continue; != P_Find3DFloor(testsec, self->Pos(), false, true, zdist2))
{
// Not on same floor
if (vilesec == corpsec || abs(zdist1 - self->Z()) > self->height)
continue;
}
} }
} }

View file

@ -129,6 +129,14 @@ struct polyblock_t;
struct FPortalGroupArray 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 enum
{ {
LOWER = 0x4000, LOWER = 0x4000,
@ -141,8 +149,9 @@ struct FPortalGroupArray
MAX_STATIC = 4 MAX_STATIC = 4
}; };
FPortalGroupArray() FPortalGroupArray(int collectionmethod = PGA_CheckPosition)
{ {
method = collectionmethod;
varused = 0; varused = 0;
inited = false; inited = false;
} }
@ -171,6 +180,7 @@ struct FPortalGroupArray
} }
bool inited; bool inited;
int method;
private: private:
WORD entry[MAX_STATIC]; WORD entry[MAX_STATIC];

View file

@ -2249,11 +2249,14 @@ void DPusher::Tick ()
// Seek out all pushable things within the force radius of this // Seek out all pushable things within the force radius of this
// point pusher. Crosses sectors, so use blockmap. // point pusher. Crosses sectors, so use blockmap.
FBlockThingsIterator it(FBoundingBox(m_X, m_Y, m_Radius)); FPortalGroupArray check(FPortalGroupArray::PGA_NoSectorPortals); // no sector portals because this thing is utterly z-unaware.
AActor *thing; 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. // 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)); bool pusharound = ((thing->flags2 & MF2_WINDTHRUST) && !(thing->flags & MF_NOCLIP));

View file

@ -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) 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 // Keep this temporary work stuff static. This function can never be called recursively
// and this would have to be reallocated for each call otherwise. // and this would have to be reallocated for each call otherwise.
static FPortalBits processMask; static FPortalBits processMask;
static TArray<FLinePortal*> foundPortals; static TArray<FLinePortal*> foundPortals;
static TArray<int> groupsToCheck;
bool retval = false; bool retval = false;
out.inited = true; out.inited = true;
if (linkedPortals.Size() == 0) if (linkedPortals.Size() != 0)
{ {
// If there are no portals, all sectors are in group 0. processMask.setSize(linkedPortals.Size());
return false; processMask.clear();
} foundPortals.Clear();
processMask.setSize(linkedPortals.Size());
processMask.clear();
foundPortals.Clear();
int thisgroup = startgroup; int thisgroup = startgroup;
processMask.setBit(thisgroup); processMask.setBit(thisgroup);
//out.Add(thisgroup); //out.Add(thisgroup);
for (unsigned i = 0; i < linkedPortals.Size(); i++) 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--)
{ {
if (processMask.getBit(foundPortals[i]->mOrigin->frontsector->PortalGroup) && line_t *ld = linkedPortals[i]->mOrigin;
!processMask.getBit(foundPortals[i]->mDestination->frontsector->PortalGroup)) 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); if (processMask.getBit(foundPortals[i]->mOrigin->frontsector->PortalGroup) &&
out.Add(foundPortals[i]->mDestination->frontsector->PortalGroup); !processMask.getBit(foundPortals[i]->mDestination->frontsector->PortalGroup))
foundone = true; {
retval = true; processMask.setBit(foundPortals[i]->mDestination->frontsector->PortalGroup);
foundPortals.Delete(i); out.Add(foundPortals[i]->mDestination->frontsector->PortalGroup);
foundone = true;
retval = true;
foundPortals.Delete(i);
}
} }
} }
} }
sector_t *sec = P_PointInSector(position.x, position.y); if (out.method != FPortalGroupArray::PGA_NoSectorPortals)
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; sector_t *sec = P_PointInSector(position.x, position.y);
fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup); sector_t *wsec = sec;
fixed_t dx = position.x + pos.x; while (!wsec->PortalBlocksMovement(sector_t::ceiling) && upperz > wsec->SkyBoxes[sector_t::ceiling]->threshold)
fixed_t dy = position.y + pos.y; {
processMask.setBit(othersec->PortalGroup); sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector;
out.Add(othersec->PortalGroup|FPortalGroupArray::UPPER); fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup);
wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat fixed_t dx = position.x + pos.x;
retval = true; fixed_t dy = position.y + pos.y;
} processMask.setBit(othersec->PortalGroup);
wsec = sec; out.Add(othersec->PortalGroup | FPortalGroupArray::UPPER);
while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold) wsec = P_PointInSector(dx, dy); // get upper sector at the exact spot we want to check and repeat
{ retval = true;
sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector; }
fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup); wsec = sec;
fixed_t dx = position.x + pos.x; while (!wsec->PortalBlocksMovement(sector_t::floor) && position.z < wsec->SkyBoxes[sector_t::floor]->threshold)
fixed_t dy = position.y + pos.y; {
processMask.setBit(othersec->PortalGroup|FPortalGroupArray::LOWER); sector_t *othersec = wsec->SkyBoxes[sector_t::floor]->Sector;
out.Add(othersec->PortalGroup); fixedvec2 pos = Displacements.getOffset(startgroup, othersec->PortalGroup);
wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat fixed_t dx = position.x + pos.x;
retval = true; 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; return retval;
} }

View file

@ -5570,10 +5570,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive)
} }
else else
{ {
FBlockThingsIterator it(FBoundingBox(self->X(), self->Y(), distance)); FPortalGroupArray check(FPortalGroupArray::PGA_Full3d);
while ((thing = it.Next())) 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); ACTION_RETURN_INT(given);