diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index f0aedd5c6..e7b32bb23 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -524,6 +524,7 @@ void P_SerializeWorld (FArchive &arc) { linePortals.Clear(); } + P_CollectLinkedPortals(); } void extsector_t::Serialize(FArchive &arc) diff --git a/src/portal.cpp b/src/portal.cpp index d9156514d..1ce6a630d 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -18,6 +18,7 @@ CVAR(Int, sv_portal_recursions, 4, CVAR_ARCHIVE|CVAR_SERVERINFO) FDisplacementTable Displacements; TArray linePortals; +TArray linkedPortals; // only the linked portals, this is used to speed up looking for them in P_CollectConnectedGroups. FArchive &operator<< (FArchive &arc, FLinePortal &port) @@ -168,6 +169,19 @@ void P_UpdatePortal(FLinePortal *port) } } +void P_CollectLinkedPortals() +{ + linkedPortals.Clear(); + for (unsigned i = 0; i < linePortals.Size(); i++) + { + FLinePortal * port = &linePortals[i]; + if (port->mType == PORTT_LINKED) + { + linkedPortals.Push(port); + } + } +} + void P_FinalizePortals() { for (unsigned i = 0; i < linePortals.Size(); i++) @@ -175,6 +189,7 @@ void P_FinalizePortals() FLinePortal * port = &linePortals[i]; P_UpdatePortal(port); } + P_CollectLinkedPortals(); } static bool ChangePortalLine(line_t *line, int destid) @@ -839,6 +854,88 @@ void P_CreateLinkedPortals() //BuildBlockmap(); } + +//============================================================================ +// +// Collect all portal groups this actor would occupy at the given position +// This is used to determine which parts of the map need to be checked. +// +//============================================================================ + +bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupTable &out) +{ + TArray foundPortals; + bool retval = false; + if (linePortals.Size() == 0) + { + return false; + } + out.setSize(Displacements.size); + out.setBit(actor->Sector->PortalGroup); + //FBoundingBox box(newx, newy, actor->radius); + int thisgroup = actor->Sector->PortalGroup; + for (unsigned i = 0; i < linePortals.Size(); i++) + { + if (linePortals[i].mType != PORTT_LINKED) continue; // not a linked portal + line_t *ld = linePortals[i].mOrigin; + int othergroup = ld->frontsector->PortalGroup; + FDisplacement &disp = Displacements(thisgroup, othergroup); + if (!disp.isSet) continue; // no connection. + + FBoundingBox box(newx + disp.x, newy + disp.y, actor->radius); + + 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(linePortals[i].mOrigin) != -1) continue; // not touched + foundPortals.Push(&linePortals[i]); + } + bool foundone = true; + while (foundone) + { + foundone = false; + for (int i = foundPortals.Size() - 1; i >= 0; i--) + { + if (out.getBit(foundPortals[i]->mOrigin->frontsector->PortalGroup) && + !out.getBit(foundPortals[i]->mDestination->frontsector->PortalGroup)) + { + out.setBit(foundPortals[i]->mDestination->frontsector->PortalGroup); + foundone = true; + retval = true; + foundPortals.Delete(i); + } + } + } + sector_t *sec = P_PointInSector(newx, newy); + sector_t *wsec = sec; + while (!wsec->PortalBlocksMovement(sector_t::ceiling) && actor->Top() > wsec->SkyBoxes[sector_t::ceiling]->threshold) + { + sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; + FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup); + fixed_t dx = newx + disp.x; + fixed_t dy = newx + disp.y; + out.setBit(othersec->PortalGroup); + 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) && actor->Z() < wsec->SkyBoxes[sector_t::floor]->threshold) + { + sector_t *othersec = wsec->SkyBoxes[sector_t::ceiling]->Sector; + FDisplacement &disp = Displacements(actor->Sector->PortalGroup, othersec->PortalGroup); + fixed_t dx = newx + disp.x; + fixed_t dy = newx + disp.y; + out.setBit(othersec->PortalGroup); + wsec = P_PointInSector(dx, dy); // get lower sector at the exact spot we want to check and repeat, + retval = true; + } + return retval; +} + + CCMD(dumplinktable) { for (int x = 1; x < Displacements.size; x++) @@ -854,3 +951,4 @@ CCMD(dumplinktable) + diff --git a/src/portal.h b/src/portal.h index 893f3ae69..faee141e5 100644 --- a/src/portal.h +++ b/src/portal.h @@ -7,6 +7,7 @@ #include "actor.h" #include "p_local.h" #include "m_bbox.h" +#include "a_sharedglobal.h" struct FDisplacement { @@ -33,6 +34,40 @@ struct FDisplacementTable } }; +extern FDisplacementTable Displacements; + +struct FPortalGroupTable +{ + TArray data; + TArray touchingGroups; + + void setSize(int num) + { + data.Resize((num + 31) / 32); + memset(&data[0], 0, data.Size()*sizeof(DWORD)); + } + + void clear() + { + data.Clear(); + touchingGroups.Clear(); + } + + void setBit(int group) + { + if (!getBit(group)) + { + data[group >> 5] |= (1 << (group & 31)); + touchingGroups.Push(group); + } + } + + int getBit(int group) + { + return data[group >> 5] & (1 << (group & 31)); + } +}; + enum { PORTF_VISIBLE = 1, @@ -60,6 +95,12 @@ enum PORG_CEILING, }; +enum +{ + PCOLL_NOTLINKED = 1, + PCOLL_LINKED = 2 +}; + struct FLinePortal { line_t *mOrigin; @@ -78,6 +119,12 @@ void P_SpawnLinePortal(line_t* line); void P_FinalizePortals(); bool P_ChangePortal(line_t *ln, int thisid, int destid); void P_CreateLinkedPortals(); +bool P_CollectConnectedGroups(AActor *actor, fixed_t newx, fixed_t newy, FPortalGroupTable &out); +void P_CollectLinkedPortals(); +inline int P_NumPortalGroups() +{ + return Displacements.size; +} /* code ported from prototype */ @@ -145,4 +192,22 @@ inline int line_t::getPortalAlignment() const return portalindex >= linePortals.Size() ? 0 : linePortals[portalindex].mAlign; } +inline bool sector_t::PortalBlocksView(int plane) +{ + if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; + return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); +} + +inline bool sector_t::PortalBlocksMovement(int plane) +{ + if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; + return !!(planes[plane].Flags & (PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); +} + +inline bool sector_t::PortalBlocksSound(int plane) +{ + if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; + return !!(planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); +} + #endif \ No newline at end of file diff --git a/src/r_defs.h b/src/r_defs.h index d65b654c2..7339b26ae 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -739,24 +739,9 @@ struct sector_t Flags &= ~SECF_SPECIALFLAGS; } - inline bool PortalBlocksView(int plane) - { - if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; - return !!(planes[plane].Flags & (PLANEF_NORENDER | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); - } - - inline bool PortalBlocksMovement(int plane) - { - if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; - return !!(planes[plane].Flags & (PLANEF_NOPASS | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); - } - - inline bool PortalBlocksSound(int plane) - { - if (SkyBoxes[plane] == NULL || SkyBoxes[plane]->special1 != SKYBOX_LINKEDPORTAL) return true; - return !!(planes[plane].Flags & (PLANEF_BLOCKSOUND | PLANEF_DISABLED | PLANEF_OBSTRUCTED)); - } - + bool PortalBlocksView(int plane); + bool PortalBlocksMovement(int plane); + bool PortalBlocksSound(int plane); int GetTerrain(int pos) const;