diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index cbb5b902c..6541e9a02 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -119,6 +119,7 @@ Note: All fields default to false unless mentioned otherwise. blockhitscan = ; // Line blocks hitscan attacks locknumber = ; // Line special is locked arg0str = ; // Alternate string-based version of arg0 + moreids = ; // Additional line IDs, specified as a space separated list of numbers (e.g. "2 666 1003 4505") transparent = ; // true = line is a Strife transparent line (alpha 0.25) @@ -201,6 +202,7 @@ Note: All fields default to false unless mentioned otherwise. // sound sequence thing in the sector will override this property. hidden = ; // if true this sector will not be drawn on the textured automap. waterzone = ; // Sector is under water and swimmable + moreids = ; // Additional sector IDs/tags, specified as a space separated list of numbers (e.g. "2 666 1003 4505") * Note about dropactors @@ -370,6 +372,9 @@ Added transparent line property (to be folded back to core UDMF standard), and h Added plane equations for sector slopes. (Please read carefully to ensure proper use!) Changed language describing the DIALOGUE lump to mention USDF as an option. +1.25 19.04.2015 +Added 'moreids' for linedefs and sectors. + =============================================================================== EOF =============================================================================== diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index 95d4f4e30..6583d1510 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -2235,7 +2235,7 @@ void FParser::SF_LineTrigger() maplinedef_t mld; mld.special=intvalue(t_argv[0]); mld.tag=t_argc > 1 ? intvalue(t_argv[1]) : 0; - P_TranslateLineDef(&line, &mld, false); + P_TranslateLineDef(&line, &mld); P_ExecuteSpecial(line.special, NULL, Script->trigger, false, line.args[0],line.args[1],line.args[2],line.args[3],line.args[4]); } @@ -4385,7 +4385,7 @@ void FParser::SF_SetLineTrigger() mld.tag = tag; mld.flags = 0; int f = lines[i].flags; - P_TranslateLineDef(&lines[i], &mld, false); + P_TranslateLineDef(&lines[i], &mld); lines[i].flags = (lines[i].flags & (ML_MONSTERSCANACTIVATE | ML_REPEAT_SPECIAL | ML_SPAC_MASK | ML_FIRSTSIDEONLY)) | (f & ~(ML_MONSTERSCANACTIVATE | ML_REPEAT_SPECIAL | ML_SPAC_MASK | ML_FIRSTSIDEONLY)); diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index 42d79dcc0..cc76beaa3 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -844,7 +844,7 @@ void P_Spawn3DFloors (void) { if (line->args[1]&8) { - line->SetMainId(line->args[4]); + tagManager.AddLineID(i, line->args[4]); } else { diff --git a/src/p_3dmidtex.cpp b/src/p_3dmidtex.cpp index ff0d3a181..9a9492e08 100644 --- a/src/p_3dmidtex.cpp +++ b/src/p_3dmidtex.cpp @@ -167,7 +167,7 @@ void P_Attach3dMidtexLinesToSector(sector_t *sector, int lineid, int tag, bool c { line_t *ln = sectors[sec].lines[line]; - if (lineid != 0 && !ln->HasId(lineid)) continue; + if (lineid != 0 && !tagManager.LineHasID(ln, lineid)) continue; if (ln->frontsector == NULL || ln->backsector == NULL || !(ln->flags & ML_3DMIDTEX)) { diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index a24c93a3b..37d6d6058 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -2063,7 +2063,7 @@ static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int { int wallnum = scroller->GetWallNum (); - if (wallnum >= 0 && sides[wallnum].linedef->HasId(id) && + if (wallnum >= 0 && tagManager.LineHasID(sides[wallnum].linedef, id) && int(sides[wallnum].linedef->sidedef[sidechoice] - sides) == wallnum && Where == scroller->GetScrollParts()) { @@ -2082,7 +2082,7 @@ static void SetWallScroller (int id, int sidechoice, fixed_t dx, fixed_t dy, int while ( (collect.Obj = iterator.Next ()) ) { if ((collect.RefNum = ((DScroller *)collect.Obj)->GetWallNum ()) != -1 && - sides[collect.RefNum].linedef->HasId(id) && + tagManager.LineHasID(sides[collect.RefNum].linedef, id) && int(sides[collect.RefNum].linedef->sidedef[sidechoice] - sides) == collect.RefNum && Where == ((DScroller *)collect.Obj)->GetScrollParts()) { diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 53cf79456..480494320 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -409,8 +409,13 @@ void P_SerializeWorld (FArchive &arc) arc << li->flags << li->activation << li->special - << li->Alpha - << li->id; + << li->Alpha; + + if (SaveVersion < 4523) + { + int id; + arc << id; + } if (P_IsACSSpecial(li->special)) { P_SerializeACSScriptNumber(arc, li->args[0], false); diff --git a/src/p_sectors.cpp b/src/p_sectors.cpp index c38834950..904c76c1b 100644 --- a/src/p_sectors.cpp +++ b/src/p_sectors.cpp @@ -825,38 +825,6 @@ sector_t *sector_t::GetHeightSec() const } -void line_t::SetMainId(int newid) -{ - id = newid; -} - -void line_t::ClearIds() -{ - id = -1; -} - -bool line_t::HasId(int checkid) const -{ - return id == checkid; -} - - -void line_t::HashIds() -{ - // killough 4/17/98: same thing, only for linedefs - int i; - - for (i=numlines; --i>=0; ) // Initially make all slots empty. - lines[i].firstid = -1; - for (i=numlines; --i>=0; ) // Proceed from last to first linedef - { // so that lower linedefs appear first - int j = (unsigned) lines[i].id % (unsigned) numlines; // Hash func - lines[i].nextid = lines[j].firstid; // Prepend linedef to chain - lines[j].firstid = i; - } -} - - bool secplane_t::CopyPlaneIfValid (secplane_t *dest, const secplane_t *opp) const { bool copy = false; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 7fa3fbcc4..166230e3d 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1907,54 +1907,59 @@ void P_AdjustLine (line_t *ld) } } -void P_SetLineID (line_t *ld) +void P_SetLineID (int i, line_t *ld) { // [RH] Set line id (as appropriate) here // for Doom format maps this must be done in P_TranslateLineDef because // the tag doesn't always go into the first arg. if (level.maptype == MAPTYPE_HEXEN) { + int setid = -1; switch (ld->special) { case Line_SetIdentification: if (!(level.flags2 & LEVEL2_HEXENHACK)) { - ld->SetMainId(ld->args[0] + 256 * ld->args[4]); + setid = ld->args[0] + 256 * ld->args[4]; ld->flags |= ld->args[1]<<16; } else { - ld->SetMainId(ld->args[0]); + setid = ld->args[0]; } ld->special = 0; break; case TranslucentLine: - ld->SetMainId(ld->args[0]); + setid = ld->args[0]; ld->flags |= ld->args[3]<<16; break; case Teleport_Line: case Scroll_Texture_Model: - ld->SetMainId(ld->args[0]); + setid = ld->args[0]; break; case Polyobj_StartLine: - ld->SetMainId(ld->args[3]); + setid = ld->args[3]; break; case Polyobj_ExplicitLine: - ld->SetMainId(ld->args[4]); + setid = ld->args[4]; break; case Plane_Align: - ld->SetMainId(ld->args[2]); + setid = ld->args[2]; break; case Static_Init: - if (ld->args[1] == Init_SectorLink) ld->SetMainId(ld->args[0]); + if (ld->args[1] == Init_SectorLink) setid = ld->args[0]; break; } + if (setid != -1) + { + tagManager.AddLineID(i, setid); + } } } @@ -2037,7 +2042,7 @@ void P_FinishLoadingLineDef(line_t *ld, int alpha) { for (j = 0; j < numlines; j++) { - if (lines[j].HasId(ld->args[0])) + if (tagManager.LineHasID(j, ld->args[0])) { lines[j].Alpha = alpha; if (additive) @@ -2139,13 +2144,13 @@ void P_LoadLineDefs (MapData * map) mld = (maplinedef_t *)mldf; ld = lines; - for (i = numlines; i > 0; i--, mld++, ld++) + for (i = 0; i < numlines; i++, mld++, ld++) { ld->Alpha = FRACUNIT; // [RH] Opaque by default // [RH] Translate old linedef special and flags to be // compatible with the new format. - P_TranslateLineDef (ld, mld, true); + P_TranslateLineDef (ld, mld, i); ld->v1 = &vertexes[LittleShort(mld->v1)]; ld->v2 = &vertexes[LittleShort(mld->v2)]; @@ -2231,13 +2236,12 @@ void P_LoadLineDefs2 (MapData * map) ld->v1 = &vertexes[LittleShort(mld->v1)]; ld->v2 = &vertexes[LittleShort(mld->v2)]; ld->Alpha = FRACUNIT; // [RH] Opaque by default - ld->ClearIds(); P_SetSideNum (&ld->sidedef[0], LittleShort(mld->sidenum[0])); P_SetSideNum (&ld->sidedef[1], LittleShort(mld->sidenum[1])); P_AdjustLine (ld); - P_SetLineID(ld); + P_SetLineID(i, ld); P_SaveLineSpecial (ld); if (level.flags2 & LEVEL2_CLIPMIDTEX) ld->flags |= ML_CLIP_MIDTEX; if (level.flags2 & LEVEL2_WRAPMIDTEX) ld->flags |= ML_WRAP_MIDTEX; @@ -3209,7 +3213,6 @@ static void P_GroupLines (bool buildmap) times[4].Clock(); // killough 1/30/98: Create xref tables for tags tagManager.HashTags(); - line_t::HashIds(); times[4].Unclock(); times[5].Clock(); diff --git a/src/p_setup.h b/src/p_setup.h index 20caa5d76..cbd423a10 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -115,7 +115,7 @@ struct line_t; struct maplinedef_t; void P_LoadTranslator(const char *lumpname); -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, bool setlineid); +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, int lineindexforid = -1); int P_TranslateSectorSpecial (int); int GetUDMFInt(int type, int index, const char *key); diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 4b3c1093d..81f4a27e4 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -232,7 +232,7 @@ bool P_ActivateLine (line_t *line, AActor *mo, int side, int activationType) !repeat && // only non-repeatable triggers (specialGeneric_Crusher) && // not for Boom's generalized linedefs special && // not for lines without a special - line->HasId(line->args[0]) && // Safety check: exclude edited UDMF linedefs or ones that don't map the tag to args[0] + tagManager.LineHasID(line, line->args[0]) && // Safety check: exclude edited UDMF linedefs or ones that don't map the tag to args[0] line->args[0] && // only if there's a tag (which is stored in the first arg) P_FindFirstSectorFromTag (line->args[0]) == -1) // only if no sector is tagged to this linedef { diff --git a/src/p_tags.cpp b/src/p_tags.cpp index 409d48291..f4164184f 100644 --- a/src/p_tags.cpp +++ b/src/p_tags.cpp @@ -50,6 +50,11 @@ static inline int sectindex(const sector_t *sector) return (int)(intptr_t)(sector - sectors); } +static inline int lineindex(const line_t *line) +{ + return (int)(intptr_t)(line - lines); +} + //----------------------------------------------------------------------------- // // @@ -58,6 +63,8 @@ static inline int sectindex(const sector_t *sector) void FTagManager::AddSectorTag(int sector, int tag) { + if (tag == 0) return; + // This function assumes that all tags for a single sector get added sequentially. // Should there ever be some need for compatibility.txt to add tags to sectors which already have a tag this function needs to be changed to adjust the startForSector indices. while (startForSector.Size() <= (unsigned int)sector) @@ -108,14 +115,50 @@ void FTagManager::RemoveSectorTags(int sect) // //----------------------------------------------------------------------------- +void FTagManager::AddLineID(int line, int tag) +{ + if (tag == -1) return; // For line IDs -1 means 'not set', unlike sectors. + + // This function assumes that all ids for a single line get added sequentially. + while (startForLine.Size() <= (unsigned int)line) + { + startForLine.Push(-1); + } + if (startForLine[line] == -1) + { + startForLine[line] = allIDs.Size(); + } + else + { + // check if the key was already defined + for (unsigned i = startForLine[line]; i < allIDs.Size(); i++) + { + if (allIDs[i].tag == tag) + { + return; + } + } + } + FTagItem it = { line, tag, -1 }; + allIDs.Push(it); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + void FTagManager::HashTags() { // add an end marker so we do not need to check for the array's size in the other functions. static FTagItem it = { -1, -1, -1 }; allTags.Push(it); + allIDs.Push(it); // Initially make all slots empty. memset(TagHashFirst, -1, sizeof(TagHashFirst)); + memset(IDHashFirst, -1, sizeof(IDHashFirst)); // Proceed from last to first so that lower targets appear first for (int i = allTags.Size() - 1; i >= 0; i--) @@ -127,6 +170,17 @@ void FTagManager::HashTags() TagHashFirst[hash] = i; } } + + for (int i = allIDs.Size() - 1; i >= 0; i--) + { + if (allIDs[i].target > 0) // only link valid entries + { + int hash = ((unsigned int)allIDs[i].tag) % FTagManager::TAG_HASH_SIZE; + allIDs[i].nexttag = IDHashFirst[hash]; + IDHashFirst[hash] = i; + } + } + } //----------------------------------------------------------------------------- @@ -184,6 +238,37 @@ bool FTagManager::SectorHasTag(const sector_t *sector, int tag) const return SectorHasTag(sectindex(sector), tag); } +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::LineHasID(int i, int tag) const +{ + if (LineHasIDs(i)) + { + int ndx = startForLine[i]; + while (allIDs[ndx].target == i) + { + if (allIDs[ndx].tag == tag) return true; + ndx++; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FTagManager::LineHasID(const line_t *line, int tag) const +{ + return LineHasID(lineindex(line), tag); +} + //----------------------------------------------------------------------------- // // RETURN NEXT SECTOR # THAT LINE TAG REFERS TO @@ -237,10 +322,10 @@ int FSectorTagIterator::NextCompat(bool compat, int start) int FLineIdIterator::Next() { - while (start != -1 && lines[start].id != searchtag) start = lines[start].nextid; + while (start >= 0 && tagManager.allIDs[start].tag != searchtag) start = tagManager.allIDs[start].nexttag; if (start == -1) return -1; int ret = start; - start = lines[start].nextid; + start = start = tagManager.allIDs[start].nexttag; return ret; } diff --git a/src/p_tags.h b/src/p_tags.h index 9b380e012..7a63ca74b 100644 --- a/src/p_tags.h +++ b/src/p_tags.h @@ -12,6 +12,7 @@ struct FTagItem }; class FSectorTagIterator; +class FLineIdIterator; class FTagManager { @@ -21,12 +22,14 @@ class FTagManager }; friend class FSectorTagIterator; + friend class FLineIdIterator; TArray allTags; TArray allIDs; TArray startForSector; TArray startForLine; int TagHashFirst[TAG_HASH_SIZE]; + int IDHashFirst[TAG_HASH_SIZE]; bool SectorHasTags(int sect) const { @@ -49,9 +52,12 @@ public: bool SectorHasTag(int sector, int tag) const; bool SectorHasTag(const sector_t *sector, int tag) const; - bool LineHasID(int line, int id); + bool LineHasID(int line, int id) const; + bool LineHasID(const line_t *line, int id) const; + void HashTags(); void AddSectorTag(int sector, int tag); + void AddLineID(int line, int tag); void RemoveSectorTags(int sect); }; @@ -99,7 +105,7 @@ public: FLineIdIterator(int id) { searchtag = id; - start = lines[(unsigned) id % (unsigned) numlines].firstid; + start = tagManager.IDHashFirst[((unsigned int)id) % FTagManager::TAG_HASH_SIZE]; } int Next(); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index 3e8f6ad60..8992c3984 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -755,7 +755,7 @@ public: mld.flags = 0; mld.special = th->special; mld.tag = th->args[0]; - P_TranslateLineDef(&ld, &mld, true); + P_TranslateLineDef(&ld, &mld); th->special = ld.special; memcpy(th->args, ld.args, sizeof (ld.args)); } @@ -780,10 +780,10 @@ public: bool strifetrans2 = false; FString arg0str, arg1str; int lineid; // forZDoomTranslated namespace + FString tagstring; memset(ld, 0, sizeof(*ld)); ld->Alpha = FRACUNIT; - ld->ClearIds(); 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; @@ -817,7 +817,7 @@ public: case NAME_Id: lineid = CheckInt(key); - ld->SetMainId(lineid); + tagManager.AddLineID(index, lineid); continue; case NAME_Sidefront: @@ -1040,22 +1040,17 @@ public: Flag(ld->flags, ML_3DMIDTEX_IMPASS, key); continue; + case NAME_MoreIds: + // delay parsing of the tag string until parsing of the sector is complete + // This ensures that the ID is always the first tag in the list. + tagstring = CheckString(key); + break; + + default: break; } -#if 0 // for later - if (namespace_bits & (Zd)) && !strnicmp(key.GetChars(), "Id", 2)) - { - char *endp; - int num = strtol(key.GetChars(), &endp, 10); - if (num > 0 && *endp == NULL) - { - // only allow ID## with ## as a proper number - ld->SetId((short)CheckInt(key), false); - } - } -#endif if ((namespace_bits & (Zd | Zdt)) && !strnicmp("user_", key.GetChars(), 5)) { @@ -1063,6 +1058,17 @@ public: } } + if (tagstring.IsNotEmpty()) + { + FScanner sc; + sc.OpenMem("tagstring", tagstring.GetChars(), tagstring.Len()); + // scan the string as long as valid numbers can be found + while (sc.CheckNumber()) + { + if (sc.Number != 0) tagManager.AddLineID(index, sc.Number); + } + } + if (isTranslated) { int saved = ld->flags; @@ -1071,7 +1077,7 @@ public: memset(&mld, 0, sizeof(mld)); mld.special = ld->special; mld.tag = lineid; - P_TranslateLineDef(ld, &mld, false); + P_TranslateLineDef(ld, &mld); ld->flags = saved | (ld->flags&(ML_MONSTERSCANACTIVATE|ML_REPEAT_SPECIAL|ML_FIRSTSIDEONLY)); } if (passuse && (ld->activation & SPAC_Use)) diff --git a/src/p_xlat.cpp b/src/p_xlat.cpp index 040ce47db..6e1718273 100644 --- a/src/p_xlat.cpp +++ b/src/p_xlat.cpp @@ -60,7 +60,7 @@ typedef enum PushMany, } triggertype_e; -void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, bool setid) +void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, int lineindexforid) { unsigned short special = (unsigned short) LittleShort(mld->special); short tag = LittleShort(mld->tag); @@ -100,13 +100,13 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld, bool setid) } flags = newflags; - if (setid) + if (lineindexforid >= 0) { // For purposes of maintaining BOOM compatibility, each // line also needs to have its ID set to the same as its tag. // An external conversion program would need to do this more // intelligently. - ld->SetMainId(tag); + tagManager.AddLineID(lineindexforid, tag); } // 0 specials are never translated. diff --git a/src/r_defs.h b/src/r_defs.h index 076f7736d..afda92089 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -888,21 +888,12 @@ struct line_t DWORD activation; // activation type int special; fixed_t Alpha; // <--- translucency (0=invisibile, FRACUNIT=opaque) - int id; // <--- same as tag or set with Line_SetIdentification int args[5]; // <--- hexen-style arguments (expanded to ZDoom's full width) - int firstid, nextid; side_t *sidedef[2]; - //DWORD sidenum[2]; // sidenum[1] will be NO_SIDE if one sided fixed_t bbox[4]; // bounding box, for the extent of the LineDef. sector_t *frontsector, *backsector; int validcount; // if == validcount, already checked int locknumber; // [Dusk] lock number for special - - - void SetMainId(int newid); - void ClearIds(); - bool HasId(int id) const; - static void HashIds(); }; // phares 3/14/98 diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index b63ba567f..a006d04a1 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4520,7 +4520,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_LineEffect) if ((oldjunk.special = special)) // Linedef type { oldjunk.tag = tag; // Sector tag for linedef - P_TranslateLineDef(&junk, &oldjunk, false); // Turn into native type + P_TranslateLineDef(&junk, &oldjunk); // Turn into native type res = !!P_ExecuteSpecial(junk.special, NULL, self, false, junk.args[0], junk.args[1], junk.args[2], junk.args[3], junk.args[4]); if (res && !(junk.flags & ML_REPEAT_SPECIAL)) // If only once,