diff --git a/src/actionspecials.h b/src/actionspecials.h index a9813c24f..7e783051e 100644 --- a/src/actionspecials.h +++ b/src/actionspecials.h @@ -139,7 +139,6 @@ DEFINE_SPECIAL(Sector_ChangeSound, 140, 2, 2, 2) DEFINE_SPECIAL(Teleport_NoStop, 154, 2, 3, 3) // portal specials -DEFINE_SPECIAL(Line_SetVisualPortal, 155, -1, -1, 3) DEFINE_SPECIAL(Line_SetPortal, 156, -1, -1, 3) // GZDoom/Vavoom specials // Although ZDoom doesn't support them it's better to have them defined so that diff --git a/src/autosegs.h b/src/autosegs.h index 61872610c..f62632139 100644 --- a/src/autosegs.h +++ b/src/autosegs.h @@ -35,6 +35,8 @@ #ifndef AUTOSEGS_H #define AUTOSEGS_H +#include "doomtype.h" + #define REGMARKER(x) (x) typedef void * const REGINFO; typedef void * NCREGINFO; @@ -73,11 +75,11 @@ class FAutoSegIterator } Probe = Head; } - NCREGINFO operator*() const + NCREGINFO operator*() const NO_SANITIZE { return *Probe; } - FAutoSegIterator &operator++() + FAutoSegIterator &operator++() NO_SANITIZE { do { diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 5f784e6de..b6ae7d0bb 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2723,9 +2723,9 @@ static bool LoadDehSupp () else { TArray &args = sym->Variants[0].ArgFlags; - if (args.Size() != 0 && !(args[0] & VARF_Optional)) + if (args.Size() > 3 && !(args[3] & VARF_Optional)) { - sc.ScriptError("Incompatible code pointer '%s'", sc.String); + sc.ScriptMessage("Incompatible code pointer '%s' %d, %d", sc.String, args.Size(), args.Size() > 3? args[3] : 0); } } Actions.Push(sym); diff --git a/src/dobjtype.cpp b/src/dobjtype.cpp index cc0a077f2..d9af4044a 100644 --- a/src/dobjtype.cpp +++ b/src/dobjtype.cpp @@ -1986,7 +1986,7 @@ END_POINTERS // //========================================================================== -static int STACK_ARGS cregcmp (const void *a, const void *b) +static int STACK_ARGS cregcmp (const void *a, const void *b) NO_SANITIZE { const PClass *class1 = *(const PClass **)a; const PClass *class2 = *(const PClass **)b; diff --git a/src/doomtype.h b/src/doomtype.h index 06541f526..0d9a13ff4 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -113,6 +113,16 @@ typedef TMap FClassMap; #define NOVTABLE #endif +#if defined(__clang__) +#if defined(__has_feature) && __has_feature(address_sanitizer)) +#define NO_SANITIZE __attribute__((no_sanitize("address"))) +#else +#define NO_SANITIZE +#endif +#else +#define NO_SANITIZE +#endif + #include "basictypes.h" // Bounding box coordinate storage. 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/g_doomedmap.cpp b/src/g_doomedmap.cpp index 3273c0331..3f42562bb 100644 --- a/src/g_doomedmap.cpp +++ b/src/g_doomedmap.cpp @@ -69,7 +69,7 @@ const char *SpecialMapthingNames[] = { "$CopyCeilingPlane", "$VertexFloorZ", "$VertexCeilingZ", - "$EDFThing", + "$EDThing", }; //========================================================================== diff --git a/src/info.h b/src/info.h index f101d66d6..2d0c627ec 100644 --- a/src/info.h +++ b/src/info.h @@ -310,7 +310,7 @@ enum ESpecialMapthings SMT_CopyCeilingPlane, SMT_VertexFloorZ, SMT_VertexCeilingZ, - SMT_EDFThing, + SMT_EDThing, }; diff --git a/src/name.cpp b/src/name.cpp index 5ab361d42..359f3398c 100644 --- a/src/name.cpp +++ b/src/name.cpp @@ -182,7 +182,7 @@ void FName::NameManager::InitBuckets () // Register built-in names. 'None' must be name 0. for (size_t i = 0; i < countof(PredefinedNames); ++i) { - assert((NULL == FindName(PredefinedNames[i], true)) && "Predefined name already inserted"); + assert((0 == FindName(PredefinedNames[i], true)) && "Predefined name already inserted"); FindName (PredefinedNames[i], false); } } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index e53b8dec6..7115b611c 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -8299,9 +8299,6 @@ scriptwait: line->args[4] = STACK(1); DPrintf("Set special on line %d (id %d) to %d(%d,%d,%d,%d,%d)\n", linenum, STACK(7), specnum, arg0, STACK(4), STACK(3), STACK(2), STACK(1)); - - // [ZZ] re-link with portals (in case this was something related to portal specials) - P_CheckPortal(line); } sp -= 7; } diff --git a/src/p_buildmap.cpp b/src/p_buildmap.cpp index e72d87d06..295dff831 100644 --- a/src/p_buildmap.cpp +++ b/src/p_buildmap.cpp @@ -630,11 +630,6 @@ static void LoadWalls (walltype *walls, int numwalls, sectortype *bsec) } } - // [ZZ] set initial line portal link - // (even though this is rather hard to happen... build doesn't have portals in our sense and it's walls don't get translated into anything like this) - for (int i = 0; i < numlines; i++) - P_CheckPortal(&lines[i]); - // Finish setting sector properties that depend on walls for (i = 0; i < numsectors; ++i, ++bsec) { diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 8c99d4d3b..e2c957097 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -3781,7 +3781,7 @@ int P_ExecuteSpecial(int num, int arg4, int arg5) { - if (num >= 0 && num < countof(LineSpecials)) + if (num >= 0 && num < (int)countof(LineSpecials)) { return LineSpecials[num](line, activator, backSide, arg1, arg2, arg3, arg4, arg5); } diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 8683ea8e6..a5f8a2948 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); @@ -477,6 +478,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; + if (SaveVersion >= 4531) { arc << li->skybox; @@ -516,6 +523,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 a8925364d..746e50e40 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1769,7 +1769,7 @@ void P_LoadThings (MapData * map) #ifndef NO_EDATA - if (mti[i].info != NULL && mti[i].info->Special == SMT_EDFThing) + if (mti[i].info != NULL && mti[i].info->Special == SMT_EDThing) { ProcessEDMapthing(&mti[i], flags); } @@ -1976,7 +1976,6 @@ void P_SetLineID (int i, line_t *ld) break; case Line_SetPortal: - case Line_SetVisualPortal: setid = ld->args[1]; // 0 = target id, 1 = this id, 2 = plane anchor break; } @@ -2079,9 +2078,6 @@ void P_FinishLoadingLineDef(line_t *ld, int alpha) ld->special = 0; break; } - - // [ZZ] check initial portal link - P_CheckPortal(ld); } // killough 4/4/98: delay using sidedefs until they are loaded void P_FinishLoadingLineDefs () @@ -2174,6 +2170,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. @@ -2266,6 +2263,8 @@ void P_LoadLineDefs2 (MapData * map) { int j; + ld->portalindex = UINT_MAX; + for (j = 0; j < 5; j++) ld->args[j] = mld->args[j]; @@ -3395,6 +3394,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 a12f1c4e9..01ef4a8f6 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -64,6 +64,7 @@ #include "a_keys.h" #include "c_dispatch.h" #include "r_sky.h" +#include "portal.h" #ifndef NO_EDATA #include "edata.h" #endif @@ -1465,6 +1466,10 @@ void P_SpawnSpecials (void) } break; + case Line_SetPortal: + P_SpawnLinePortal(&lines[i]); + break; + // [RH] ZDoom Static_Init settings case Static_Init: switch (lines[i].args[1]) @@ -1536,6 +1541,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 7e8405d57..79d0a5f7d 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -6,97 +6,35 @@ #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) -PortalDrawseg* CurrentPortal = NULL; -int CurrentPortalUniq = 0; -bool CurrentPortalInSkybox = false; +TArray linePortals; -// [ZZ] lots of floats here to avoid overflowing a lot -bool R_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, - fixed_t& rx, fixed_t& ry) + +FArchive &operator<< (FArchive &arc, FLinePortal &port) { - float xx = FIXED2FLOAT(o2x) - FIXED2FLOAT(o1x); - float xy = FIXED2FLOAT(o2y) - FIXED2FLOAT(o1y); - - float d1x = FIXED2FLOAT(p1x) - FIXED2FLOAT(o1x); - float d1y = FIXED2FLOAT(p1y) - FIXED2FLOAT(o1y); - - if (d1x > d1y) - { - d1y = d1y / d1x * 32767.0f; - d1x = 32767.0; - } - else - { - d1x = d1x / d1y * 32767.0f; - d1y = 32767.0; - } - - float d2x = FIXED2FLOAT(p2x) - FIXED2FLOAT(o2x); - float d2y = FIXED2FLOAT(p2y) - FIXED2FLOAT(o2y); - - float cross = d1x*d2y - d1y*d2x; - if (fabs(cross) < 1e-8) - return false; - - float t1 = (xx * d2y - xy * d2x)/cross; - rx = o1x + FLOAT2FIXED(d1x * t1); - ry = o1y + FLOAT2FIXED(d1y * t1); - return true; + arc << port.mOrigin + << port.mDestination + << port.mXDisplacement + << port.mYDisplacement + << port.mType + << port.mFlags + << port.mDefFlags + << port.mAlign; + return arc; } -inline int P_PointOnLineSideExplicit (fixed_t x, fixed_t y, fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) + +void P_SpawnLinePortal(line_t* line) { - return DMulScale32 (y-y1, x2-x1, x1-x, y2-y1) > 0; -} + // portal destination is special argument #0 + line_t* dst = NULL; -bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t viewy, bool partial, bool samebehind) -{ - // check if this line is between portal and the viewer. clip away if it is. - bool behind1 = !!P_PointOnLineSide(line->v1->x, line->v1->y, portal); - bool behind2 = !!P_PointOnLineSide(line->v2->x, line->v2->y, portal); - - // [ZZ] update 16.12.2014: if a vertex equals to one of portal's vertices, it's treated as being behind the portal. - // this is required in order to clip away diagonal lines around the portal (example: 1-sided triangle shape with a mirror on it's side) - if ((line->v1->x == portal->v1->x && line->v1->y == portal->v1->y) || - (line->v1->x == portal->v2->x && line->v1->y == portal->v2->y)) - behind1 = samebehind; - if ((line->v2->x == portal->v1->x && line->v2->y == portal->v1->y) || - (line->v2->x == portal->v2->x && line->v2->y == portal->v2->y)) - behind2 = samebehind; - - if (behind1 && behind2) + if (line->args[2] >= PORTT_VISUAL && line->args[2] <= PORTT_LINKED) { - // line is behind the portal plane. now check if it's in front of two view plane borders (i.e. if it will get in the way of rendering) - fixed_t dummyx, dummyy; - bool infront1 = R_IntersectLines(line->v1->x, line->v1->y, line->v2->x, line->v2->y, viewx, viewy, portal->v1->x, portal->v1->y, dummyx, dummyy); - bool infront2 = R_IntersectLines(line->v1->x, line->v1->y, line->v2->x, line->v2->y, viewx, viewy, portal->v2->x, portal->v2->y, dummyx, dummyy); - if (infront1 && infront2) - return true; - } - - return false; -} - -bool P_CheckPortal(line_t* line) -{ - if (line->special == Line_Mirror) - { - line->portal = true; - line->portal_mirror = true; - line->portal_passive = true; - line->portal_dst = line; - } - else if (line->special == Line_SetPortal || - line->special == Line_SetVisualPortal) - { - // portal destination is special argument #0 - line_t* dst = NULL; - if (line->args[0] > 0) { int linenum = -1; @@ -113,30 +51,161 @@ bool P_CheckPortal(line_t* line) } } - if (dst) + 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) { - line->portal = true; - line->portal_mirror = false; - line->portal_passive = (line->special == Line_SetVisualPortal); - line->portal_dst = dst; + port->mDefFlags = port->mType == PORTT_VISUAL ? PORTF_VISIBLE : port->mType == PORTT_TELEPORT ? PORTF_TYPETELEPORT : PORTF_TYPEINTERACTIVE; + + } - else + } + 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++) { - line->portal = false; - line->portal_mirror = false; - line->portal_passive = false; - line->portal_dst = NULL; + 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; + + // we need to create the backlink here, too. + lines[i].portalindex = linePortals.Reserve(1); + port = &linePortals.Last(); + + memset(port, 0, sizeof(FLinePortal)); + port->mOrigin = &lines[i]; + port->mDestination = line; + port->mType = PORTT_LINKED; + port->mAlign = PORG_ABSOLUTE; + port->mDefFlags = PORTF_TYPEINTERACTIVE; + + } } } else { - line->portal = false; - line->portal_mirror = false; - line->portal_passive = false; - line->portal_dst = NULL; + // 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, + fixed_t& rx, fixed_t& ry) +{ + double xx = FIXED2DBL(o2x) - FIXED2DBL(o1x); + double xy = FIXED2DBL(o2y) - FIXED2DBL(o1y); + + double d1x = FIXED2DBL(p1x) - FIXED2DBL(o1x); + double d1y = FIXED2DBL(p1y) - FIXED2DBL(o1y); + + if (d1x > d1y) + { + d1y = d1y / d1x * 32767.0f; + d1x = 32767.0; + } + else + { + d1x = d1x / d1y * 32767.0f; + d1y = 32767.0; } - return (line->portal); + double d2x = FIXED2DBL(p2x) - FIXED2DBL(o2x); + double d2y = FIXED2DBL(p2y) - FIXED2DBL(o2y); + + double cross = d1x*d2y - d1y*d2x; + if (fabs(cross) < 1e-8) + return false; + + double t1 = (xx * d2y - xy * d2x)/cross; + rx = o1x + FLOAT2FIXED(d1x * t1); + ry = o1y + FLOAT2FIXED(d1y * t1); + return true; +} + +inline int P_PointOnLineSideExplicit (fixed_t x, fixed_t y, fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2) +{ + return DMulScale32 (y-y1, x2-x1, x1-x, y2-y1) > 0; +} + +bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t viewy, bool partial, bool samebehind) +{ + // check if this line is between portal and the viewer. clip away if it is. + bool behind1 = !!P_PointOnLineSidePrecise(line->v1->x, line->v1->y, portal); + bool behind2 = !!P_PointOnLineSidePrecise(line->v2->x, line->v2->y, portal); + + // [ZZ] update 16.12.2014: if a vertex equals to one of portal's vertices, it's treated as being behind the portal. + // this is required in order to clip away diagonal lines around the portal (example: 1-sided triangle shape with a mirror on it's side) + if ((line->v1->x == portal->v1->x && line->v1->y == portal->v1->y) || + (line->v1->x == portal->v2->x && line->v1->y == portal->v2->y)) + behind1 = samebehind; + if ((line->v2->x == portal->v1->x && line->v2->y == portal->v1->y) || + (line->v2->x == portal->v2->x && line->v2->y == portal->v2->y)) + behind2 = samebehind; + + if (behind1 && behind2) + { + // line is behind the portal plane. now check if it's in front of two view plane borders (i.e. if it will get in the way of rendering) + fixed_t dummyx, dummyy; + bool infront1 = P_IntersectLines(line->v1->x, line->v1->y, line->v2->x, line->v2->y, viewx, viewy, portal->v1->x, portal->v1->y, dummyx, dummyy); + bool infront2 = P_IntersectLines(line->v1->x, line->v1->y, line->v2->x, line->v2->y, viewx, viewy, portal->v2->x, portal->v2->y, dummyx, dummyy); + if (infront1 && infront2) + return true; + } + + return false; } void P_TranslatePortalXY(line_t* src, line_t* dst, fixed_t& x, fixed_t& y) @@ -211,17 +280,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; } } @@ -244,9 +318,9 @@ fixed_t P_PointLineDistance(line_t* line, fixed_t x, fixed_t y) void P_NormalizeVXVY(fixed_t& vx, fixed_t& vy) { - float _vx = FIXED2FLOAT(vx); - float _vy = FIXED2FLOAT(vy); - float len = sqrt(_vx*_vx+_vy*_vy); + double _vx = FIXED2DBL(vx); + double _vy = FIXED2DBL(vy); + double len = sqrt(_vx*_vx+_vy*_vy); vx = FLOAT2FIXED(_vx/len); vy = FLOAT2FIXED(_vy/len); } @@ -302,12 +376,12 @@ bool PortalTracer::TraceStep() { li = in->d.line; - if (li->portal && !li->portal_passive) + if (li->isLinePortal()) { if (P_PointOnLineSide(startx-dirx, starty-diry, li)) continue; // we're at the back side of this line - line_t* out = li->portal_dst; + line_t* out = li->getPortalDestination(); this->in = li; this->out = out; @@ -361,7 +435,7 @@ bool PortalTracer::TraceStep() } } - //Printf("returning %d; vx = %.2f; vy = %.2f\n", (oDepth != depth), FIXED2FLOAT(this->vx), FIXED2FLOAT(this->vy)); + //Printf("returning %d; vx = %.2f; vy = %.2f\n", (oDepth != depth), FIXED2DBL(this->vx), FIXED2DBL(this->vy)); return (oDepth != depth); // if a portal has been found, return false } diff --git a/src/portal.h b/src/portal.h index 103e3072b..6a947fd0e 100644 --- a/src/portal.h +++ b/src/portal.h @@ -8,29 +8,54 @@ #include "p_local.h" #include "m_bbox.h" -/* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ -struct PortalDrawseg + +enum { - line_t* src; // source line (the one drawn) this doesn't change over render loops - line_t* dst; // destination line (the one that the portal is linked with, equals 'src' for mirrors) + PORTF_VISIBLE = 1, + PORTF_PASSABLE = 2, + PORTF_SOUNDTRAVERSE = 4, + PORTF_INTERACTIVE = 8, - int x1; // drawseg x1 - int x2; // drawseg x2 - - int len; - TArray ceilingclip; - TArray floorclip; - - bool mirror; // true if this is a mirror (src should equal dst) + PORTF_TYPETELEPORT = PORTF_VISIBLE | PORTF_PASSABLE | PORTF_SOUNDTRAVERSE, + PORTF_TYPEINTERACTIVE = PORTF_VISIBLE | PORTF_PASSABLE | PORTF_SOUNDTRAVERSE | PORTF_INTERACTIVE, }; -extern PortalDrawseg* CurrentPortal; -extern int CurrentPortalUniq; -extern bool CurrentPortalInSkybox; +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 */ bool P_ClipLineToPortal(line_t* line, line_t* portal, fixed_t viewx, fixed_t viewy, bool partial = true, bool samebehind = true); -bool P_CheckPortal(line_t* line); void P_TranslatePortalXY(line_t* src, line_t* dst, fixed_t& x, fixed_t& y); void P_TranslatePortalVXVY(line_t* src, line_t* dst, fixed_t& vx, fixed_t& vy); void P_TranslatePortalAngle(line_t* src, line_t* dst, angle_t& angle); @@ -71,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 60efbc1dd..f073498d4 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; @@ -590,7 +590,7 @@ void R_AddLine (seg_t *line) rw_havehigh = rw_havelow = false; // Single sided line? - if (backsector == NULL || (line->linedef->portal && line->sidedef == line->linedef->sidedef[0])) + if (backsector == NULL || (line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0])) { solid = true; } diff --git a/src/r_bsp.h b/src/r_bsp.h index 3b2a399e3..8c19a2670 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -107,8 +107,6 @@ extern size_t FirstInterestingDrawseg; extern int WindowLeft, WindowRight; extern WORD MirrorFlags; -extern TArray WallPortals; - typedef void (*drawfunc_t) (int start, int stop); EXTERN_CVAR (Bool, r_drawflat) // [RH] Don't texture segs? diff --git a/src/r_defs.h b/src/r_defs.h index 4f09d053f..ce358dab3 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -379,6 +379,7 @@ enum PLANEF_NOPASS = 16, PLANEF_BLOCKSOUND = 32, PLANEF_DISABLED = 64, + PLANEF_OBSTRUCTED = 128, // if the portal plane is beyond the sector's floor or ceiling. }; // Internal sector flags @@ -1084,16 +1085,15 @@ 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; - bool portal_mirror; - bool portal_passive; + unsigned portalindex; TObjPtr skybox; - bool isLinePortal() const - { - return portal; - } + // returns true if the portal is crossable by actors + bool isLinePortal() const; + // returns true if the portal needs to be handled by the renderer + bool isVisualPortal() const; + line_t *getPortalDestination() const; + int getPortalAlignment() const; }; // phares 3/14/98 diff --git a/src/r_main.cpp b/src/r_main.cpp index 4c2b89c0a..883ab13dc 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -37,6 +37,7 @@ #include "r_local.h" #include "r_plane.h" #include "r_bsp.h" +#include "r_segs.h" #include "r_3dfloors.h" #include "r_sky.h" #include "st_stuff.h" diff --git a/src/r_segs.cpp b/src/r_segs.cpp index 1f09dbf18..96a0595d7 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -67,6 +67,9 @@ CVAR(Bool, r_np2, true, 0) extern fixed_t globaluclip, globaldclip; +PortalDrawseg* CurrentPortal = NULL; +int CurrentPortalUniq = 0; +bool CurrentPortalInSkybox = false; // OPTIMIZE: closed two sided lines as single sided @@ -517,7 +520,7 @@ void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) wallupper[i] = mceilingclip[i]; } mceilingclip = wallupper; - } + } if (fake3D & FAKE3D_CLIPBOTTOM) { OWallMost(walllower, sclipBottom - viewz, &WallC); @@ -2001,8 +2004,7 @@ void R_NewWall (bool needlights) midtexture = toptexture = bottomtexture = 0; if (sidedef == linedef->sidedef[0] && - linedef->portal && - (!linedef->portal_mirror || r_drawmirrors)) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals + (linedef->isVisualPortal() || (linedef->special == Line_Mirror && r_drawmirrors))) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals { markfloor = markceiling = true; // act like an one-sided wall here (todo: check how does this work with transparency) rw_markportal = true; @@ -2629,7 +2631,7 @@ void R_StoreWallRange (int start, int stop) { PortalDrawseg pds; pds.src = curline->linedef; - pds.dst = curline->linedef->portal_dst; + pds.dst = curline->linedef->special == Line_Mirror? curline->linedef : curline->linedef->getPortalDestination(); pds.x1 = ds_p->x1; pds.x2 = ds_p->x2; pds.len = pds.x2 - pds.x1; @@ -2650,7 +2652,7 @@ void R_StoreWallRange (int start, int stop) pds.floorclip[i] = RenderTarget->GetHeight()-1; } - pds.mirror = curline->linedef->portal_mirror; + pds.mirror = curline->linedef->special == Line_Mirror; WallPortals.Push(pds); } @@ -2761,11 +2763,6 @@ int OWallMost (short *mostbuf, fixed_t z, const FWallCoords *wallc) } #endif #endif - if (mostbuf[ix1] < 0) mostbuf[ix1] = 0; - else if (mostbuf[ix1] > viewheight) mostbuf[ix1] = (short)viewheight; - if (mostbuf[ix2-1] < 0) mostbuf[ix2-1] = 0; - else if (mostbuf[ix2-1] > viewheight) mostbuf[ix2-1] = (short)viewheight; - return bad; } @@ -2919,11 +2916,6 @@ int WallMost (short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, y + centeryfrac,yinc); } - if (mostbuf[ix1] < 0) mostbuf[ix1] = 0; - else if (mostbuf[ix1] > viewheight) mostbuf[ix1] = (short)viewheight; - if (mostbuf[ix2-1] < 0) mostbuf[ix2-1] = 0; - else if (mostbuf[ix2-1] > viewheight) mostbuf[ix2-1] = (short)viewheight; - return bad; } diff --git a/src/r_segs.h b/src/r_segs.h index b8bf96511..e2f8d7611 100644 --- a/src/r_segs.h +++ b/src/r_segs.h @@ -49,4 +49,25 @@ extern fixed_t rw_lightstep; extern fixed_t rw_lightleft; extern fixed_t rw_offset; +/* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ +struct PortalDrawseg +{ + line_t* src; // source line (the one drawn) this doesn't change over render loops + line_t* dst; // destination line (the one that the portal is linked with, equals 'src' for mirrors) + + int x1; // drawseg x1 + int x2; // drawseg x2 + + int len; + TArray ceilingclip; + TArray floorclip; + + bool mirror; // true if this is a mirror (src should equal dst) +}; + +extern PortalDrawseg* CurrentPortal; +extern int CurrentPortalUniq; +extern bool CurrentPortalInSkybox; +extern TArray WallPortals; + #endif diff --git a/src/r_things.cpp b/src/r_things.cpp index 003e3a758..682ad5f35 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -31,6 +31,7 @@ #include #include +#include "p_lnspec.h" #include "templates.h" #include "doomdef.h" #include "m_swap.h" @@ -339,11 +340,11 @@ static inline bool R_ClipSpriteColumnWithPortals (fixed_t x, fixed_t y, vissprit if (!seg->curline) continue; line_t* line = seg->curline->linedef; - // divline? wtf, anyway, divlines aren't supposed to be drawn. But I definitely saw NULL linedefs in drawsegs. + // ignore minisegs from GL nodes. if (!line) continue; // check if this line will clip sprites to itself - if (!line->portal) + if (!line->isVisualPortal() && line->special != Line_Mirror) continue; // don't clip sprites with portal's back side (it's transparent) @@ -739,7 +740,7 @@ void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor // [ZZ] Or less definitely not visible (hue) // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. - if (!CurrentPortalInSkybox && CurrentPortal && !!P_PointOnLineSide(thing->X(), thing->Y(), CurrentPortal->dst)) + if (!CurrentPortalInSkybox && CurrentPortal && !!P_PointOnLineSidePrecise(thing->X(), thing->Y(), CurrentPortal->dst)) return; // [RH] Interpolate the sprite's position to make it look smooth 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/sc_man.cpp b/src/sc_man.cpp index 03b52a050..9268404fb 100644 --- a/src/sc_man.cpp +++ b/src/sc_man.cpp @@ -856,7 +856,7 @@ int FScanner::MustMatchString (const char * const *strings, size_t stride) i = MatchString (strings, stride); if (i == -1) { - ScriptError (NULL); + ScriptError ("Unknown keyword '%s'", String); } return i; } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index ba5bc7f47..a3e4ac6fa 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -676,7 +676,7 @@ static void DoJump(AActor *self, AActor *stateowner, FState *callingstate, FStat { P_SetPsprite(self->player, ps_flash, jumpto); } - else if (callingstate == self->state) + else if (callingstate == self->state || (self->ObjectFlags & OF_StateChanged)) { // Rather than using self->SetState(jumpto) to set the state, // set the state directly. Since this function is only called by diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index d1f7e75da..691d4b872 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -892,6 +892,19 @@ public: ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); }; +//========================================================================== +// +// FxReturnStatement +// +//========================================================================== + +class FxReturnStatement : public FxTailable +{ +public: + FxReturnStatement(const FScriptPosition &pos); + ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); +}; + //========================================================================== // // diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 2a756a68c..19c02837c 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3484,6 +3484,21 @@ ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build, bool tailcall) // //========================================================================== +FxReturnStatement::FxReturnStatement(const FScriptPosition &pos) +: FxTailable(pos) +{ +} + +ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build, bool tailcall) +{ + build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); + return ExpEmit(); +} + +//========================================================================== +// +//========================================================================== + FxClassTypeCast::FxClassTypeCast(const PClass *dtype, FxExpression *x) : FxExpression(x->ScriptPosition) { diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index a44fa1ed1..d6f3eb939 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -386,6 +386,12 @@ FxTailable *ParseActions(FScanner &sc, FState state, FString statestring, Baggag } add = new FxIfStatement(cond, true_part, false_part, sc); } + else if (sc.Compare("return")) + { // Handle a return statement + sc.MustGetStringName(";"); + sc.MustGetString(); + add = new FxReturnStatement(sc); + } else { // Handle a regular action function call add = ParseAction(sc, state, statestring, bag); diff --git a/src/version.h b/src/version.h index bb3ab0386..5d8ee12df 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) diff --git a/wadsrc/static/mapinfo/eternity.txt b/wadsrc/static/mapinfo/eternity.txt index ad0bab886..6dc624a29 100644 --- a/wadsrc/static/mapinfo/eternity.txt +++ b/wadsrc/static/mapinfo/eternity.txt @@ -3,6 +3,6 @@ DoomEdNums { 5003 = none - 5004 = "$EDFThing" + 5004 = "$EDThing" } diff --git a/wadsrc/static/xlat/eternity.txt b/wadsrc/static/xlat/eternity.txt index bfe275b1f..59351e1e2 100644 --- a/wadsrc/static/xlat/eternity.txt +++ b/wadsrc/static/xlat/eternity.txt @@ -67,9 +67,7 @@ enum // Parameterized linedefs // They are never used directly in Doom-format maps. Instead, it passes through ExtraData and 270. // Hexen format is incomplete; and Quasar wants to use ZDoom-compatible special values for UDMF. -// The translation here is for the odd EDF that specifies them as numbers. - -/* +// The translation here is for the odd Extradata that specifies them as numbers. 300 = 0, Door_Raise(0) 301 = 0, Door_Open(0) 302 = 0, Door_Close(0) @@ -77,7 +75,7 @@ enum 304 = 0, Door_WaitRaise(0) 305 = 0, Door_WaitClose(0) 306 = 0, Floor_RaiseToHighest(0) -307 = 0, Floor_LowerToHighest(0) +307 = 0, Floor_LowerToHighestEE(0) 308 = 0, Floor_RaiseToLowest(0) 309 = 0, Floor_LowerToLowest(0) 310 = 0, Floor_RaiseToNearest(0) @@ -114,7 +112,6 @@ enum 341 = 0, Stairs_BuildDownDoom(0) 342 = 0, Stairs_BuildUpDoomSync(0) 343 = 0, Stairs_BuildDownDoomSync(0) -*/ // Two-way portals are not supported yet either 344 = 0, Unsupported() // "Portal_TwowayCeiling" @@ -157,11 +154,11 @@ enum 375 = 0, Radius_Quake(0) // Eternity's linked portals, horizontal link version (wall-to-wall) -376 = 0, Unsupported() // "Portal_LinkedLineToLine" -377 = 0, Unsupported() // "Portal_LinkedLineToLineAnchor" +376 = 0, Line_SetPortal(0, tag, 4) // "Portal_LinkedLineToLine" +377 = 0, Line_SetPortal(1, tag, 4) // "Portal_LinkedLineToLineAnchor" // The famous Hexen linedef -// 378 = Line_SetIdentification +378 = 0, Line_SetIdentification(0) // Attached sectors == linked sectors; However, the implementation in Eternity // is based on front sectors of tagged lines, not on sector tags. So instead