diff --git a/src/farchive.h b/src/farchive.h index 64c7cc9fe..b6a223bab 100644 --- a/src/farchive.h +++ b/src/farchive.h @@ -286,9 +286,11 @@ template<> inline FArchive &operator<< (FArchive &arc, FFont* &font) struct FStrifeDialogueNode; struct FSwitchDef; struct FDoorAnimation; +struct FLinePortal; template<> FArchive &operator<< (FArchive &arc, FStrifeDialogueNode *&node); template<> FArchive &operator<< (FArchive &arc, FSwitchDef* &sw); template<> FArchive &operator<< (FArchive &arc, FDoorAnimation* &da); +FArchive &operator<< (FArchive &arc, FLinePortal &da); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 31eb76c7a..f0aedd5c6 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -53,6 +53,7 @@ #include "p_lnspec.h" #include "p_acs.h" #include "p_terrain.h" +#include "portal.h" static void CopyPlayer (player_t *dst, player_t *src, const char *name); static void ReadOnePlayer (FArchive &arc, bool skipload); @@ -474,6 +475,12 @@ void P_SerializeWorld (FArchive &arc) } arc << li->args[1] << li->args[2] << li->args[3] << li->args[4]; + if (SaveVersion >= 4532) + { + arc << li->portalindex; + } + else li->portalindex = UINT_MAX; + for (j = 0; j < 2; j++) { if (li->sidedef[j] == NULL) @@ -508,6 +515,15 @@ void P_SerializeWorld (FArchive &arc) { arc << zn->Environment; } + + if (SaveVersion >= 4532) + { + arc << linePortals; + } + else + { + linePortals.Clear(); + } } void extsector_t::Serialize(FArchive &arc) diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 6229d811c..d1519d031 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -2156,6 +2156,7 @@ void P_LoadLineDefs (MapData * map) for (i = 0; i < numlines; i++, mld++, ld++) { ld->Alpha = FRACUNIT; // [RH] Opaque by default + ld->portalindex = UINT_MAX; // [RH] Translate old linedef special and flags to be // compatible with the new format. @@ -2236,6 +2237,8 @@ void P_LoadLineDefs2 (MapData * map) { int j; + ld->portalindex = UINT_MAX; + for (j = 0; j < 5; j++) ld->args[j] = mld->args[j]; @@ -3365,6 +3368,7 @@ void P_FreeLevelData () level.killed_monsters = level.found_items = level.found_secrets = wminfo.maxfrags = 0; + linePortals.Clear(); FBehavior::StaticUnloadModules (); if (vertexes != NULL) { diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 15182661b..833e3b632 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -1504,6 +1504,7 @@ void P_SpawnSpecials (void) } // [RH] Start running any open scripts on this map FBehavior::StaticStartTypedScripts (SCRIPT_Open, NULL, false); + P_FinalizePortals(); } // killough 2/28/98: diff --git a/src/p_tags.cpp b/src/p_tags.cpp index 15528378b..30b0db819 100644 --- a/src/p_tags.cpp +++ b/src/p_tags.cpp @@ -247,6 +247,18 @@ bool FTagManager::SectorHasTag(const sector_t *sector, int tag) const // //----------------------------------------------------------------------------- +int FTagManager::GetFirstLineID(const line_t *line) const +{ + int i = lineindex(line); + return LineHasIDs(i) ? allIDs[startForLine[i]].tag : 0; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + bool FTagManager::LineHasID(int i, int tag) const { if (LineHasIDs(i)) diff --git a/src/p_tags.h b/src/p_tags.h index c3162d9b5..b351b04eb 100644 --- a/src/p_tags.h +++ b/src/p_tags.h @@ -57,6 +57,7 @@ public: bool SectorHasTag(int sector, int tag) const; bool SectorHasTag(const sector_t *sector, int tag) const; + int GetFirstLineID(const line_t *line) const; bool LineHasID(int line, int id) const; bool LineHasID(const line_t *line, int id) const; diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 366a56dcf..118ee2d95 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -792,6 +792,7 @@ public: memset(ld, 0, sizeof(*ld)); ld->Alpha = FRACUNIT; + ld->portalindex = UINT_MAX; ld->sidedef[0] = ld->sidedef[1] = NULL; if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; diff --git a/src/portal.cpp b/src/portal.cpp index 2d99a3df7..b49eb0d3a 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -6,10 +6,128 @@ #include "c_cvars.h" #include "m_bbox.h" #include "p_tags.h" +#include "farchive.h" // simulation recurions maximum CVAR(Int, sv_portal_recursions, 4, CVAR_ARCHIVE|CVAR_SERVERINFO) +TArray linePortals; + + +FArchive &operator<< (FArchive &arc, FLinePortal &port) +{ + arc << port.mOrigin + << port.mDestination + << port.mXDisplacement + << port.mYDisplacement + << port.mType + << port.mFlags + << port.mDefFlags + << port.mAlign; + return arc; +} + + +void P_SpawnLinePortal(line_t* line) +{ + // portal destination is special argument #0 + line_t* dst = NULL; + + if (line->args[2] >= PORTT_VISUAL && line->args[2] <= PORTT_LINKED) + { + if (line->args[0] > 0) + { + int linenum = -1; + + for (int i = 0; i < numlines; i++) + { + if (&lines[i] == line) + continue; + if (tagManager.LineHasID(&lines[i], line->args[0])) + { + dst = &lines[i]; + break; + } + } + } + + line->portalindex = linePortals.Reserve(1); + FLinePortal *port = &linePortals.Last(); + + memset(port, 0, sizeof(FLinePortal)); + port->mOrigin = line; + port->mDestination = dst; + port->mType = BYTE(line->args[2]); // range check is done above. + port->mAlign = BYTE(line->args[3] >= PORG_ABSOLUTE && line->args[3] <= PORG_CEILING ? line->args[3] : PORG_ABSOLUTE); + if (port->mDestination != NULL) + { + port->mDefFlags = port->mType == PORTT_VISUAL ? PORTF_VISIBLE : port->mType == PORTT_TELEPORT ? PORTF_TYPETELEPORT : PORTF_TYPEINTERACTIVE; + + + } + } + else if (line->args[2] == PORTT_LINKEDEE && line->args[0] == 0) + { + // EE-style portals require that the first line ID is identical and the first arg of the two linked linedefs are 0 and 1 respectively. + + int mytag = tagManager.GetFirstLineID(line); + + for (int i = 0; i < numlines; i++) + { + if (tagManager.GetFirstLineID(&lines[i]) == mytag && lines[i].args[0] == 1) + { + line->portalindex = linePortals.Reserve(1); + FLinePortal *port = &linePortals.Last(); + + memset(port, 0, sizeof(FLinePortal)); + port->mOrigin = line; + port->mDestination = &lines[i]; + port->mType = PORTT_LINKED; + port->mAlign = PORG_ABSOLUTE; + port->mDefFlags = PORTF_TYPEINTERACTIVE; + } + } + } + else + { + // undefined type + return; + } +} + +void P_UpdatePortal(FLinePortal *port) +{ + if (port->mDestination == NULL) + { + // Portal has no destination: switch it off + port->mFlags = 0; + } + else if (port->mDestination->getPortalDestination() != port->mOrigin) + { + //portal doesn't link back. This will be a simple teleporter portal. + port->mFlags = port->mDefFlags & ~PORTF_INTERACTIVE; + if (port->mType == PORTT_LINKED) + { + // this is illegal. Demote the type to TELEPORT + port->mType = PORTT_TELEPORT; + port->mDefFlags &= ~PORTF_INTERACTIVE; + } + } + else + { + port->mFlags = port->mDefFlags; + } +} + +void P_FinalizePortals() +{ + for (unsigned i = 0; i < linePortals.Size(); i++) + { + FLinePortal * port = &linePortals[i]; + P_UpdatePortal(port); + } +} + // [ZZ] lots of floats here to avoid overflowing a lot bool P_IntersectLines(fixed_t o1x, fixed_t o1y, fixed_t p1x, fixed_t p1y, fixed_t o2x, fixed_t o2y, fixed_t p2x, fixed_t p2y, @@ -78,41 +196,6 @@ bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t vie return false; } -void P_SpawnLinePortal(line_t* line) -{ - // portal destination is special argument #0 - line_t* dst = NULL; - - if (line->args[0] > 0) - { - int linenum = -1; - - for (int i = 0; i < numlines; i++) - { - if (&lines[i] == line) - continue; - if (tagManager.LineHasID(&lines[i], line->args[0])) - { - dst = &lines[i]; - break; - } - } - } - - if (dst) - { - line->_portal = true; - //line->portal_passive = true;// !!(line->args[2] & PORTAL_VISUAL; (line->special == Line_SetVisualPortal); - line->_portal_dst = dst; - } - else - { - line->_portal = false; - //line->portal_passive = false; - line->_portal_dst = NULL; - } -} - void P_TranslatePortalXY(line_t* src, line_t* dst, fixed_t& x, fixed_t& y) { if (!src || !dst) @@ -185,17 +268,22 @@ void P_TranslatePortalAngle(line_t* src, line_t* dst, angle_t& angle) void P_TranslatePortalZ(line_t* src, line_t* dst, fixed_t& z) { - // args[2] = 0 - no teleport + // args[2] = 0 - no adjustment // args[2] = 1 - adjust by floor difference // args[2] = 2 - adjust by ceiling difference - if (src->args[2] == 1) + switch (src->getPortalAlignment()) { + case PORG_FLOOR: z = z - src->frontsector->floorplane.ZatPoint(src->v1->x, src->v1->y) + dst->frontsector->floorplane.ZatPoint(dst->v2->x, dst->v2->y); - } - else if (src->args[2] == 2) - { + return; + + case PORG_CEILING: z = z - src->frontsector->ceilingplane.ZatPoint(src->v1->x, src->v1->y) + dst->frontsector->ceilingplane.ZatPoint(dst->v2->x, dst->v2->y); + return; + + default: + return; } } diff --git a/src/portal.h b/src/portal.h index 3b707f543..6a947fd0e 100644 --- a/src/portal.h +++ b/src/portal.h @@ -8,7 +8,50 @@ #include "p_local.h" #include "m_bbox.h" + +enum +{ + PORTF_VISIBLE = 1, + PORTF_PASSABLE = 2, + PORTF_SOUNDTRAVERSE = 4, + PORTF_INTERACTIVE = 8, + + PORTF_TYPETELEPORT = PORTF_VISIBLE | PORTF_PASSABLE | PORTF_SOUNDTRAVERSE, + PORTF_TYPEINTERACTIVE = PORTF_VISIBLE | PORTF_PASSABLE | PORTF_SOUNDTRAVERSE | PORTF_INTERACTIVE, +}; + +enum +{ + PORTT_VISUAL, + PORTT_TELEPORT, + PORTT_INTERACTIVE, + PORTT_LINKED, + PORTT_LINKEDEE // Eternity compatible definition which uses only one line ID and a different anchor type to link to. +}; + +enum +{ + PORG_ABSOLUTE, // does not align at all. z-ccoordinates must match. + PORG_FLOOR, + PORG_CEILING, +}; + +struct FLinePortal +{ + line_t *mOrigin; + line_t *mDestination; + fixed_t mXDisplacement; + fixed_t mYDisplacement; + BYTE mType; + BYTE mFlags; + BYTE mDefFlags; + BYTE mAlign; +}; + +extern TArray linePortals; + void P_SpawnLinePortal(line_t* line); +void P_FinalizePortals(); /* code ported from prototype */ @@ -53,4 +96,27 @@ public: /* new code */ fixed_t P_PointLineDistance(line_t* line, fixed_t x, fixed_t y); + +// returns true if the portal is crossable by actors +inline bool line_t::isLinePortal() const +{ + return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_PASSABLE); +} + +// returns true if the portal needs to be handled by the renderer +inline bool line_t::isVisualPortal() const +{ + return portalindex >= linePortals.Size() ? false : !!(linePortals[portalindex].mFlags & PORTF_VISIBLE); +} + +inline line_t *line_t::getPortalDestination() const +{ + return portalindex >= linePortals.Size() ? (line_t*)NULL : linePortals[portalindex].mDestination; +} + +inline int line_t::getPortalAlignment() const +{ + return portalindex >= linePortals.Size() ? 0 : linePortals[portalindex].mAlign; +} + #endif \ No newline at end of file diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index a6dd7f61b..819bd9532 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -99,7 +99,7 @@ static BYTE FakeSide; int WindowLeft, WindowRight; WORD MirrorFlags; -TArray WallPortals; +TArray WallPortals(1000); // note: this array needs to go away as reallocation can cause crashes. static subsector_t *InSubsector; diff --git a/src/r_defs.h b/src/r_defs.h index 901dadcb0..a000fb8e3 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -988,26 +988,14 @@ struct line_t sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked int locknumber; // [Dusk] lock number for special - - line_t *_portal_dst; - bool _portal; + unsigned portalindex; // returns true if the portal is crossable by actors - bool isLinePortal() const - { - return false; // right now there are no crossable portals - } - + bool isLinePortal() const; // returns true if the portal needs to be handled by the renderer - bool isVisualPortal() const - { - return _portal; - } - - line_t *getPortalDestination() const - { - return _portal_dst; - } + bool isVisualPortal() const; + line_t *getPortalDestination() const; + int getPortalAlignment() const; }; // phares 3/14/98 diff --git a/src/r_utility.h b/src/r_utility.h index 2d9aac086..10dcf5352 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -58,6 +58,11 @@ inline int R_PointOnSide (fixed_t x, fixed_t y, const node_t *node) angle_t R_PointToAngle2 (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); inline angle_t R_PointToAngle (fixed_t x, fixed_t y) { return R_PointToAngle2 (viewx, viewy, x, y); } +inline angle_t R_PointToAnglePrecise (fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y) +{ + return xs_RoundToUInt(atan2(double(y-viewy), double(x-viewx)) * (ANGLE_180/M_PI)); +} + subsector_t *R_PointInSubsector (fixed_t x, fixed_t y); fixed_t R_PointToDist2 (fixed_t dx, fixed_t dy); void R_ResetViewInterpolation (); diff --git a/src/version.h b/src/version.h index b4b3ace12..3af1c1b7f 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4531 +#define SAVEVER 4532 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x)