diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 2716d1eb14..6f33c718e3 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -163,6 +163,13 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize, sc.MustGetString(); iwad->BkColor = V_GetColor(NULL, sc); } + else if (sc.Compare("IgnoreTitlePatches")) + { + sc.MustGetStringName("="); + sc.MustGetNumber(); + if (sc.Number) iwad->flags |= GI_IGNORETITLEPATCHES; + else iwad->flags &= ~GI_IGNORETITLEPATCHES; + } else if (sc.Compare("Load")) { sc.MustGetStringName("="); diff --git a/src/d_main.h b/src/d_main.h index 05d818f145..bc8f54adab 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -160,7 +160,7 @@ public: int GetIWadFlags(unsigned int num) const { if (num < mIWadInfos.Size()) return mIWadInfos[num].flags; - else return false; + else return 0; } }; diff --git a/src/g_game.cpp b/src/g_game.cpp index d389dc5300..861b506a85 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -174,9 +174,7 @@ uint8_t* zdembodyend; // end of ZDEM BODY chunk bool singledemo; // quit after playing a demo from cmdline bool precache = true; // if true, load all graphics at start - -wbstartstruct_t wminfo; // parms for world map / intermission - + short consistancy[MAXPLAYERS][BACKUPTICS]; diff --git a/src/g_hub.cpp b/src/g_hub.cpp index 677b991609..0e70deb1ab 100644 --- a/src/g_hub.cpp +++ b/src/g_hub.cpp @@ -129,12 +129,13 @@ void G_LeavingHub(FLevelLocals *Level, int mode, cluster_info_t * cluster, wbsta { if (cluster->flags & CLUSTER_LOOKUPNAME) { - Level->LevelName = GStrings(cluster->ClusterName); + wbs->thisname = GStrings(cluster->ClusterName); } else { - Level->LevelName = cluster->ClusterName; + wbs->thisname = cluster->ClusterName; } + wbs->LName0.SetInvalid(); // The level's own name was just invalidated, and so was its name patch. } } } diff --git a/src/g_level.cpp b/src/g_level.cpp index 66b4b84f17..d86618ebeb 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -81,6 +81,7 @@ #include "a_dynlight.h" #include "p_conversation.h" #include "p_effect.h" +#include "stringtable.h" #include "gi.h" @@ -779,6 +780,8 @@ void G_DoCompleted (void) // Close the conversation menu if open. P_FreeStrifeConversations (); + wbstartstruct_t wminfo; // parms for world map / intermission + if (primaryLevel->DoCompleted(nextlevel, wminfo)) { gamestate = GS_INTERMISSION; @@ -802,12 +805,14 @@ bool FLevelLocals::DoCompleted (FString nextlevel, wbstartstruct_t &wminfo) if (!(flags & LEVEL_CHANGEMAPCHEAT)) FindLevelInfo (MapName)->flags |= LEVEL_VISITED; + uint32_t langtable[2] = {}; wminfo.finished_ep = cluster - 1; wminfo.LName0 = TexMan.CheckForTexture(info->PName, ETextureType::MiscPatch); + wminfo.thisname = info->LookupLevelName(&langtable[0]); // re-get the name so we have more info about its origin. wminfo.current = MapName; if (deathmatch && - (dmflags & DF_SAME_LEVEL) && + (*dmflags & DF_SAME_LEVEL) && !(flags & LEVEL_CHANGEMAPCHEAT)) { wminfo.next = MapName; @@ -818,13 +823,37 @@ bool FLevelLocals::DoCompleted (FString nextlevel, wbstartstruct_t &wminfo) level_info_t *nextinfo = FindLevelInfo (nextlevel, false); if (nextinfo == NULL || strncmp (nextlevel, "enDSeQ", 6) == 0) { - wminfo.next = nextlevel; + wminfo.next = ""; wminfo.LName1.SetInvalid(); + wminfo.nextname = ""; } else { wminfo.next = nextinfo->MapName; wminfo.LName1 = TexMan.CheckForTexture(nextinfo->PName, ETextureType::MiscPatch); + wminfo.nextname = info->LookupLevelName(&langtable[1]); + } + } + + // Ignore the (C)WILVxx lumps from the original Doom IWADs so that the name can be localized properly, if the retrieved text does not come from the default table. + // This is only active for those IWADS where the style of these graphics matches the provided BIGFONT for the respective game. + if (gameinfo.flags & GI_IGNORETITLEPATCHES) + { + FTextureID *texids[] = { &wminfo.LName0, &wminfo.LName1 }; + for (int i = 0; i < 2; i++) + { + if (texids[i]->isValid() && langtable[i] != FStringTable::default_table) + { + FTexture *tex = TexMan.GetTexture(*texids[i]); + if (tex != nullptr) + { + int filenum = Wads.GetLumpFile(tex->GetSourceLump()); + if (filenum >= 0 && filenum <= Wads.GetIwadNum()) + { + texids[i]->SetInvalid(); + } + } + } } } @@ -1296,7 +1325,7 @@ DEFINE_ACTION_FUNCTION(FLevelLocals, WorldDone) void G_DoWorldDone (void) { gamestate = GS_LEVEL; - if (wminfo.next[0] == 0) + if (nextlevel.IsEmpty()) { // Don't crash if no next map is given. Just repeat the current one. Printf ("No next map specified.\n"); diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index 826f44181d..97645273da 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -299,14 +299,14 @@ void level_info_t::Reset() // //========================================================================== -FString level_info_t::LookupLevelName() +FString level_info_t::LookupLevelName(uint32_t *langtable) { + // All IWAD names that may be substituted by a graphics patch are declared as language strings. + if (langtable) *langtable = 0; if (flags & LEVEL_LOOKUPLEVELNAME) { const char *thename; - const char *lookedup; - - lookedup = GStrings[LevelName]; + const char *lookedup = GStrings.GetString(LevelName, langtable); if (lookedup == NULL) { thename = LevelName; diff --git a/src/gamedata/g_mapinfo.h b/src/gamedata/g_mapinfo.h index bb6ae9886d..f9cd2f6f4a 100644 --- a/src/gamedata/g_mapinfo.h +++ b/src/gamedata/g_mapinfo.h @@ -400,7 +400,7 @@ struct level_info_t } void Reset(); bool isValid(); - FString LookupLevelName (); + FString LookupLevelName (uint32_t *langtable = nullptr); void ClearDefered() { deferred.Clear(); diff --git a/src/gamedata/gi.h b/src/gamedata/gi.h index 769076d4d2..4f27a6969b 100644 --- a/src/gamedata/gi.h +++ b/src/gamedata/gi.h @@ -38,15 +38,19 @@ #include "zstring.h" // Flags are not user configurable and only depend on the standard IWADs -#define GI_MAPxx 0x00000001 -#define GI_SHAREWARE 0x00000002 -#define GI_MENUHACK_EXTENDED 0x00000004 // (Heretic) -#define GI_TEASER2 0x00000008 // Alternate version of the Strife Teaser -#define GI_COMPATSHORTTEX 0x00000010 // always force COMPAT_SHORTTEX for IWAD maps. -#define GI_COMPATSTAIRS 0x00000020 // same for stairbuilding -#define GI_COMPATPOLY1 0x00000040 // Hexen's MAP36 needs old polyobject drawing -#define GI_COMPATPOLY2 0x00000080 // so does HEXDD's MAP47 -#define GI_NOTEXTCOLOR 0x00000100 // Chex Quest 3 would have everything green +enum +{ + GI_MAPxx = 0x00000001, + GI_SHAREWARE = 0x00000002, + GI_MENUHACK_EXTENDED = 0x00000004, // (Heretic) + GI_TEASER2 = 0x00000008, // Alternate version of the Strife Teaser + GI_COMPATSHORTTEX = 0x00000010, // always force COMPAT_SHORTTEX for IWAD maps. + GI_COMPATSTAIRS = 0x00000020, // same for stairbuilding + GI_COMPATPOLY1 = 0x00000040, // Hexen's MAP36 needs old polyobject drawing + GI_COMPATPOLY2 = 0x00000080, // so does HEXDD's MAP47 + GI_NOTEXTCOLOR = 0x00000100, // Chex Quest 3 would have everything green + GI_IGNORETITLEPATCHES = 0x00000200, // Ignore the map name graphics when not runnning in English language +}; #include "gametype.h" diff --git a/src/gamedata/stringtable.cpp b/src/gamedata/stringtable.cpp index 1afc68e439..679735c121 100644 --- a/src/gamedata/stringtable.cpp +++ b/src/gamedata/stringtable.cpp @@ -83,11 +83,11 @@ void FStringTable::LoadLanguage (int lumpnum) } if (len == 1 && sc.String[0] == '*') { - activeMaps.Push(MAKE_ID('*', 0, 0, 0)); + activeMaps.Push(global_table); } else if (len == 7 && stricmp (sc.String, "default") == 0) { - activeMaps.Push(MAKE_ID('*', '*', 0, 0)); + activeMaps.Push(default_table); } else { @@ -167,18 +167,18 @@ void FStringTable::UpdateLanguage() auto checkone = [&](uint32_t lang_id) { auto list = allStrings.CheckKey(lang_id); - if (list && currentLanguageSet.Find(list) == currentLanguageSet.Size()) - currentLanguageSet.Push(list); + if (list && currentLanguageSet.FindEx([&](const auto &element) { return element.first == lang_id; }) == currentLanguageSet.Size()) + currentLanguageSet.Push(std::make_pair(lang_id, list)); }; - checkone(MAKE_ID('*', '*', '*', 0)); - checkone(MAKE_ID('*', 0, 0, 0)); + checkone(dehacked_table); + checkone(global_table); for (int i = 0; i < 4; ++i) { checkone(LanguageIDs[i]); checkone(LanguageIDs[i] & MAKE_ID(0xff, 0xff, 0, 0)); } - checkone(MAKE_ID('*', '*', 0, 0)); + checkone(default_table); } // Replace \ escape sequences in a string with the escaped characters. @@ -219,7 +219,7 @@ bool FStringTable::exists(const char *name) FName nm(name, true); if (nm != NAME_None) { - uint32_t defaultStrings[] = { MAKE_ID('*', '*', '*', 0), MAKE_ID('*', 0, 0, 0), MAKE_ID('*', '*', 0, 0) }; + uint32_t defaultStrings[] = { default_table, global_table, dehacked_table }; for (auto mapid : defaultStrings) { @@ -235,7 +235,7 @@ bool FStringTable::exists(const char *name) } // Finds a string by name and returns its value -const char *FStringTable::operator[] (const char *name) const +const char *FStringTable::GetString(const char *name, uint32_t *langtable) const { if (name == nullptr || *name == 0) { @@ -246,8 +246,12 @@ const char *FStringTable::operator[] (const char *name) const { for (auto map : currentLanguageSet) { - auto item = map->CheckKey(nm); - if (item) return item->GetChars(); + auto item = map.second->CheckKey(nm); + if (item) + { + if (langtable) *langtable = map.first; + return item->GetChars(); + } } } return nullptr; diff --git a/src/gamedata/stringtable.h b/src/gamedata/stringtable.h index 11416367c1..97201a582d 100644 --- a/src/gamedata/stringtable.h +++ b/src/gamedata/stringtable.h @@ -58,25 +58,36 @@ public: class FStringTable { public: + enum : uint32_t + { + default_table = MAKE_ID('*', '*', 0, 0), + global_table = MAKE_ID('*', 0, 0, 0), + dehacked_table = MAKE_ID('*', '*', '*', 0) + }; + using LangMap = TMap; void LoadStrings (); void UpdateLanguage(); - StringMap GetDefaultStrings() { return allStrings[MAKE_ID('*', '*', 0, 0)]; } // Dehacked needs these for comparison + StringMap GetDefaultStrings() { return allStrings[default_table]; } // Dehacked needs these for comparison void SetDehackedStrings(StringMap && map) { - allStrings.Insert(MAKE_ID('*', '*', '*', 0), map); + allStrings.Insert(dehacked_table, map); UpdateLanguage(); } - + + const char *GetString(const char *name, uint32_t *langtable) const; const char *operator() (const char *name) const; // Never returns NULL - const char *operator[] (const char *name) const; // Can return NULL + const char *operator[] (const char *name) const + { + return GetString(name, nullptr); + } bool exists(const char *name); private: LangMap allStrings; - TArray currentLanguageSet; + TArray> currentLanguageSet; void LoadLanguage (int lumpnum); static size_t ProcessEscapes (char *str); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index dddfe2d46c..2842587b40 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -260,8 +260,7 @@ void FLevelLocals::ClearLevelData() ClearAllSubsectorLinks(); // can't be done as part of the polyobj deletion process. total_monsters = total_items = total_secrets = - killed_monsters = found_items = found_secrets = - wminfo.maxfrags = 0; + killed_monsters = found_items = found_secrets = 0; for (int i = 0; i < 4; i++) { @@ -375,7 +374,6 @@ void P_SetupLevel(FLevelLocals *Level, int position, bool newGame) // This is motivated as follows: Level->maptype = MAPTYPE_UNKNOWN; - wminfo.partime = 180; if (!savegamerestore) { diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 7c8f207d6c..98ff002033 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -322,7 +322,7 @@ static FxExpression *StringConstToChar(FxExpression *basex) int chr = str.GetNextCharacter(position); // Only succeed if the full string is consumed, i.e. it contains only one code point. - if (position == str.Len()) + if (position == (int)str.Len()) { return new FxConstant(chr, basex->ScriptPosition); } diff --git a/src/utility/zstring.cpp b/src/utility/zstring.cpp index 5845d47c32..fa56229ad3 100644 --- a/src/utility/zstring.cpp +++ b/src/utility/zstring.cpp @@ -485,7 +485,7 @@ void FString::DeleteLastCharacter() { if (Len() == 0) return; auto pos = Len() - 1; - while (pos > 0 && Chars[pos] >= 0x80 && Chars[pos] < 0xc0) pos--; + while (pos > 0 && uint8_t(Chars[pos]) >= 0x80 && uint8_t(Chars[pos]) < 0xc0) pos--; if (pos <= 0) { Data()->Release(); diff --git a/src/wi_stuff.cpp b/src/wi_stuff.cpp index c752be66d1..1849d471cf 100644 --- a/src/wi_stuff.cpp +++ b/src/wi_stuff.cpp @@ -760,13 +760,7 @@ void WI_Start(wbstartstruct_t *wbstartstruct) I_FatalError("Cannot create status screen"); } } - // Set up some global stuff that is always needed. - auto info = FindLevelInfo(wbstartstruct->next, false); - if (info == nullptr) - { - wbstartstruct->next = ""; - } - else wbstartstruct->nextname = info->LookupLevelName(); + V_SetBlend(0, 0, 0, 0); S_StopAllChannels(); for (auto Level : AllLevels()) @@ -863,6 +857,7 @@ DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, next_ep); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, current); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, next); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, nextname); +DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, thisname); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, LName0); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, LName1); DEFINE_FIELD_X(WBStartStruct, wbstartstruct_t, maxkills); diff --git a/src/wi_stuff.h b/src/wi_stuff.h index 4b12820f00..6f06037811 100644 --- a/src/wi_stuff.h +++ b/src/wi_stuff.h @@ -30,6 +30,7 @@ #include "doomdef.h" class FTexture; +struct FLevelLocals; // // INTERMISSION @@ -54,6 +55,7 @@ struct wbstartstruct_t FString current; // [RH] Name of map just finished FString next; // next level, [RH] actual map name FString nextname; // printable name for next level. + FString thisname; // printable name for next level. FTextureID LName0; FTextureID LName1; @@ -78,8 +80,6 @@ struct wbstartstruct_t // Intermission stats. // Parameters for world map / intermission. -extern wbstartstruct_t wminfo; - // Called by main loop, animate the intermission. void WI_Ticker (); diff --git a/wadsrc/static/iwadinfo.txt b/wadsrc/static/iwadinfo.txt index 12d5db1b56..c75fc166d1 100644 --- a/wadsrc/static/iwadinfo.txt +++ b/wadsrc/static/iwadinfo.txt @@ -318,6 +318,7 @@ IWad "DPHOOF","BFGGA0","HEADA1","CYBRA1","SPIDA1D1", "E4M2", "DMENUPIC", "M_ACPT", "M_CAN", "M_EXITO", "M_CHG" BannerColors = "54 54 54", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -330,6 +331,7 @@ IWad Compatibility = "Shorttex" MustContain = "SMOOSHED", "ANIMDEFS", "LANGUAGE", "MAPINFO", "ENDOOM", "M_DOOM", "TITLEPIC", "TEXTURES" BannerColors = "a8 00 00", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -345,6 +347,7 @@ IWad "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", "DPHOOF","BFGGA0","HEADA1","CYBRA1","SPIDA1D1", "E4M2" BannerColors = "54 54 54", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -360,6 +363,7 @@ IWad "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", "DPHOOF","BFGGA0","HEADA1","CYBRA1","SPIDA1D1" BannerColors = "54 54 54", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -371,6 +375,7 @@ IWad Compatibility = "Shareware", "Shorttex" MustContain = "E1M1" BannerColors = "54 54 54", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -384,6 +389,7 @@ IWad Compatibility = "Shorttex", "Stairs" MustContain = "MAP01", "REDTNT2" BannerColors = "a8 00 00", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -397,6 +403,7 @@ IWad Compatibility = "Shorttex" MustContain = "MAP01", "CAMO1" BannerColors = "a8 00 00", "a8 a8 a8" + IgnoreTitlePatches = 1 } IWad @@ -411,9 +418,10 @@ IWad MustContain = "MAP01", "DMENUPIC", "M_ACPT", "M_CAN", "M_EXITO", "M_CHG" BannerColors = "a8 00 00", "a8 a8 a8" Load = "nerve.wad" + IgnoreTitlePatches = 1 } -// Doom 2 must be last to be checked becaude MAP01 is its only requirement +// Doom 2 must be last to be checked because MAP01 is its only requirement IWad { Name = "DOOM 2: Hell on Earth" @@ -426,6 +434,7 @@ IWad MustContain = "MAP01" BannerColors = "a8 00 00", "a8 a8 a8" Load = "nerve.wad" + IgnoreTitlePatches = 1 } diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index fbbd2e1665..cf4fa12702 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1,6 +1,6 @@ /* U.S. English. (Sorry, it's not English English.) */ -[en default] +[default] SECRETMESSAGE = "A secret is revealed!"; diff --git a/wadsrc/static/zscript/statscreen/statscreen.txt b/wadsrc/static/zscript/statscreen/statscreen.txt index 7edde60c42..0903299abb 100644 --- a/wadsrc/static/zscript/statscreen/statscreen.txt +++ b/wadsrc/static/zscript/statscreen/statscreen.txt @@ -705,8 +705,7 @@ class StatusScreen abstract play version("2.5") Sucks = TexMan.CheckForTexture("WISUCKS", TexMan.Type_MiscPatch); // "sucks" Par = TexMan.CheckForTexture("WIPAR", TexMan.Type_MiscPatch); // "par" - // Use the local level structure which can be overridden by hubs - lnametexts[0] = Level.LevelName; + lnametexts[0] = wbstartstruct.thisname; lnametexts[1] = wbstartstruct.nextname; bg = InterBackground.Create(wbs); diff --git a/wadsrc/static/zscript/statscreen/types.txt b/wadsrc/static/zscript/statscreen/types.txt index c3dad0c765..972a47b0c5 100644 --- a/wadsrc/static/zscript/statscreen/types.txt +++ b/wadsrc/static/zscript/statscreen/types.txt @@ -23,6 +23,7 @@ struct WBStartStruct native version("2.4") native String current; // [RH] Name of map just finished native String next; // next level, [RH] actual map name native String nextname; // next level, printable name + native String thisname; // this level, printable name native TextureID LName0; native TextureID LName1; diff --git a/wadsrc_extra/static/language.enu b/wadsrc_extra/static/language.enu index 36290df475..ce0a737250 100644 --- a/wadsrc_extra/static/language.enu +++ b/wadsrc_extra/static/language.enu @@ -1,4 +1,4 @@ -[en default] +[default] // Strings from Hexen's IWAD scripts. Technically they are not needed here for English, they are mainly meant to be documentation for translating.