]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/q3compat_sanity
authorMario <mario.mario@y7mail.com>
Fri, 30 Sep 2022 09:54:07 +0000 (19:54 +1000)
committerMario <mario.mario@y7mail.com>
Fri, 30 Sep 2022 09:54:07 +0000 (19:54 +1000)
21 files changed:
qcsrc/client/mapvoting.qc
qcsrc/common/gamemodes/gamemode/clanarena/clanarena.qh
qcsrc/common/gamemodes/gamemode/duel/duel.qh
qcsrc/common/gamemodes/gamemode/freezetag/freezetag.qh
qcsrc/common/gamemodes/gamemode/tdm/tdm.qh
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/mapobjects/target/changelevel.qc
qcsrc/lib/spawnfunc.qh
qcsrc/menu/draw.qc
qcsrc/menu/draw.qh
qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc
qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc
qcsrc/menu/xonotic/maplist.qc
qcsrc/server/campaign.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/mapvoting.qc
qcsrc/server/world.qc
qcsrc/server/world.qh
xonotic-common.cfg
xonotic-server.cfg

index 29640128c0abf575c8a8b43fa03cb81c5d044e9e..e0f0e79e67fee3b43bbf7db8c2362964a1128019 100644 (file)
@@ -637,7 +637,7 @@ void GameTypeVote_ReadOption(int i)
        }
        else
        {
-               Gametype type = MapInfo_Type_FromString(gt, false);
+               Gametype type = MapInfo_Type_FromString(gt, false, false);
                mv_pk3[i] = strzone(MapInfo_Type_ToText(type));
                mv_desc[i] = MapInfo_Type_Description(type);
        }
index 3b3dace64e30be13fad4c5ded3be8ba2b5a264ad..ad5909eb297bc088b51eee3b1f82e8e0cb2dfaac 100644 (file)
@@ -26,7 +26,7 @@ CLASS(ClanArena, Gametype)
     }
     METHOD(ClanArena, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
     {
-        if(spawnpoints >= 8 && diameter > 4096)
+        if(spawnpoints >= 8 && diameter > 3250)
             return true;
         return false;
     }
index cacf13fa55c59fe6d6929445b8dad7806e841fe6..ede5f9b42d05cb9424646fbb82cd04a9e65570d1 100644 (file)
@@ -10,7 +10,7 @@ CLASS(Duel, Gametype)
     }
     METHOD(Duel, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
     {
-        return (diameter < 16384);
+        return (diameter < 3250);
     }
     METHOD(Duel, m_isForcedSupported, bool(Gametype this))
     {
index 61d3b91e9416c0653b4d4ebe04a60456dee064b6..9620ba835e1782f475a793a1e3ebdce59bfd2d54 100644 (file)
@@ -29,7 +29,7 @@ CLASS(FreezeTag, Gametype)
     }
     METHOD(FreezeTag, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
     {
-        if(spawnpoints >= 8 && diameter > 4096)
+        if(spawnpoints >= 8 && diameter > 3250)
             return true;
         return false;
     }
index d0e23940a3d769320a39672ef0cb4735579aa796..eeee581f42166f22a118be82f15673a537f4f35b 100644 (file)
@@ -23,7 +23,7 @@ CLASS(TeamDeathmatch, Gametype)
     }
     METHOD(TeamDeathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
     {
-        if(spawnpoints >= 8 && diameter > 4096)
+        if(spawnpoints >= 8 && diameter > 3250)
             return true;
         return false;
     }
index c0b67ff4d535e97e9752132fafd7134c28c9ae6d..ca90f2f33e1f2fe1f31b26698985b9308cd37c54 100644 (file)
@@ -8,6 +8,8 @@
        #include <common/monsters/_mod.qh>
 #endif
 
+int autocvar_g_mapinfo_q3compat = 1;
+
 #ifdef MENUQC
 #define WARN_COND false
 #else
@@ -271,6 +273,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
        float r;
        float diameter, spawnpoints;
        float spawnplaces;
+       bool is_q3df_map = false;
 
        vector mapMins, mapMaxs;
 
@@ -296,6 +299,28 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
        mapMins = '0 0 0';
        mapMaxs = '0 0 0';
 
+       if(autocvar_g_mapinfo_q3compat == 2) // generate mapinfo using arena data
+       {
+               // try for .arena or .defi files, as they may have more accurate information
+               bool isdefi = false;
+               float arena_fh = -1;
+               string arena_fn = _MapInfo_FindArenaFile(pFilename, ".arena");
+               if(arena_fn != "")
+                       arena_fh = fopen(arena_fn, FILE_READ);
+               if(arena_fh < 0)
+               {
+                       isdefi = true;
+                       arena_fn = _MapInfo_FindArenaFile(pFilename, ".defi");
+                       if(arena_fn != "")
+                               arena_fh = fopen(arena_fn, FILE_READ);
+               }
+               if(arena_fh >= 0)
+               {
+                       _MapInfo_ParseArena(arena_fn, arena_fh, pFilename, NULL, isdefi, true);
+                       fclose(arena_fh);
+               }
+       }
+
        for (;;)
        {
                if (!((s = fgets(fh))))
@@ -317,7 +342,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                                _MapInfo_Map_worldspawn_music = v;
                        else if(k == "noise")
                                _MapInfo_Map_worldspawn_music = v;
-                       else if(k == "message")
+                       else if(k == "message" && (!MapInfo_Map_title || MapInfo_Map_title == "<TITLE>"))
                        {
                                i = strstrofs(v, " by ", 0);
                                if(MapInfo_Map_author == "<AUTHOR>" && i >= 0)
@@ -370,6 +395,8 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                                        MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS;
                                else if(v == "target_music" || v == "trigger_music")
                                        _MapInfo_Map_worldspawn_music = string_null; // don't use regular BGM
+                               else if(v == "target_stopTimer")
+                                       is_q3df_map = true; // don't support standard gamemodes
                                else
                                        FOREACH(Gametypes, true, it.m_generate_mapinfo(it, v));
                        }
@@ -388,7 +415,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
        {
                // we have a symmetrical map, don't add the modes without bases
        }
-       else
+       else if(!is_q3df_map)
        {
                FOREACH(Gametypes, it.m_isAlwaysSupported(it, spawnpoints, diameter), MapInfo_Map_supportedGametypes |= it.m_flags);
        }
@@ -586,9 +613,10 @@ void _MapInfo_Map_ApplyGametypeEx(string s, Gametype pWantedType, Gametype pThis
        }
 }
 
-Gametype MapInfo_Type_FromString(string gtype, bool dowarn)
+Gametype MapInfo_Type_FromString(string gtype, bool dowarn, bool is_q3compat)
 {
        string replacement = "";
+       bool do_warn = true;
        switch (gtype)
        {
                case "nexball":   replacement = "nb"; break;
@@ -597,6 +625,13 @@ Gametype MapInfo_Type_FromString(string gtype, bool dowarn)
                case "invasion":  replacement = "inv"; break;
                case "assault":   replacement = "as"; break;
                case "race":      replacement = "rc"; break;
+               // quake 3 compat
+               case "ffa":       replacement = "dm"; do_warn = false; break;
+               case "cctf":
+               case "oneflag":   replacement = "ctf"; do_warn = false; break;
+               case "tournament":
+               case "tourney":   replacement = "duel"; do_warn = false; break;
+               case "arena":     if(is_q3compat) { replacement = "ca"; do_warn = false; } break;
        }
        if (replacement != "")
        {
@@ -755,6 +790,211 @@ float MapInfo_isRedundant(string fn, string t)
        return false;
 }
 
+bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gametype pGametypeToSet, bool isdefi, bool isgenerator)
+{
+       // NOTE: .arena files can hold more than 1 map's information!
+       // to handle this, we're going to store gathered information in local variables and save it if we encounter the correct map name
+       bool in_brackets = false; // testing a potential mapinfo section (within brackets)
+       bool dosave = false;
+       string stored_Map_description = "";
+       string stored_Map_title = "";
+       string stored_Map_author = "";
+       int stored_supportedGametypes = 0;
+       int stored_supportedFeatures = 0;
+       int stored_flags = 0;
+       string t, s;
+       for (;;)
+       {
+               if (!((s = fgets(fh))))
+                       break;
+
+               // catch different sorts of comments
+               if(s == "")                    // empty lines
+                       continue;
+               if(substring(s, 0, 2) == "//") // C++ style
+                       continue;
+               if(strstrofs(s, "{", 0) >= 0)
+               {
+                       if(in_brackets)
+                               return false; // edge case? already in a bracketed section!
+                       in_brackets = true;
+                       continue;
+               }
+               else if(!in_brackets)
+               {
+                       // if we're not inside a bracket, don't process map info
+                       continue;
+               }
+               if(strstrofs(s, "}", 0) >= 0)
+               {
+                       if(!in_brackets)
+                               return false; // no starting bracket! let the mapinfo generation system handle it
+                       in_brackets = false;
+                       if(dosave)
+                       {
+                               MapInfo_Map_description = stored_Map_description;
+                               if(stored_Map_title != "")
+                                       MapInfo_Map_title = stored_Map_title;
+                               MapInfo_Map_author = stored_Map_author;
+                               if(isgenerator)
+                                       MapInfo_Map_supportedGametypes = stored_supportedGametypes;
+                               else
+                               {
+                                       FOREACH(Gametypes, it.m_flags & stored_supportedGametypes,
+                                       {
+                                               _MapInfo_Map_ApplyGametype ("", pGametypeToSet, it, true);
+                                       });
+                               }
+                               MapInfo_Map_supportedFeatures = stored_supportedFeatures;
+                               MapInfo_Map_flags = stored_flags;
+                               return true; // no need to continue through the file, we have our map!
+                       }
+                       else
+                       {
+                               // discard any gathered locals, we're not using the correct map!
+                               stored_Map_description = "";
+                               stored_Map_title = "";
+                               stored_Map_author = "";
+                               stored_supportedGametypes = 0;
+                               stored_supportedFeatures = 0;
+                               stored_flags = 0;
+                               continue;
+                       }
+               }
+
+               s = strreplace("\t", " ", s);
+
+               float p = strstrofs(s, "//", 0);
+               if(p >= 0)
+                       s = substring(s, 0, p);
+
+               // perform an initial trim to ensure the first argument is properly obtained
+               //   remove leading spaces
+               while(substring(s, 0, 1) == " ")
+                       s = substring(s, 1, -1);
+
+               t = car(s); s = cdr(s);
+               t = strtolower(t); // apparently some q3 maps use capitalized parameters
+
+               //   remove trailing spaces
+               while(substring(t, -1, 1) == " ")
+                       t = substring(t, 0, -2);
+
+               //   remove trailing spaces
+               while(substring(s, -1, 1) == " ")
+                       s = substring(s, 0, -2);
+               //   remove leading spaces
+               while(substring(s, 0, 1) == " ")
+                       s = substring(s, 1, -1);
+               // limited support of ""
+               //   remove trailing and leading " of s
+               if(substring(s, 0, 1) == "\"")
+               {
+                       if(substring(s, -1, 1) == "\"")
+                               s = substring(s, 1, -2);
+               }
+               if(t == "longname")
+                       stored_Map_title = s;
+               else if(t == "author")
+                       stored_Map_author = s;
+               else if(t == "type")
+               {
+                       // if there is a valid gametype in this .arena file, include it in the menu
+                       stored_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;
+                       // type in quake 3 holds all the supported gametypes, so we must loop through all of them
+                       // TODO: handle support here better to include more Xonotic teamplay modes
+                       string types = s;
+                       types = strreplace("team", "tdm ft", types);
+                       types = strreplace("ffa", "dm lms ka", types);
+                       if(strstrofs(types, "tournament", 0) < 0 && strstrofs(types, "tdm", 0) >= 0) // larger team map, support additional gamemodes!
+                               types = cons(types, "ca kh");
+                       FOREACH_WORD(types, true,
+                       {
+                               Gametype f = MapInfo_Type_FromString(it, false, true);
+                               if(f)
+                                       stored_supportedGametypes |= f.m_flags;
+                       });
+               }
+               else if(t == "style" && isdefi)
+               {
+                       // we have a defrag map on our hands, add CTS!
+                       // TODO: styles
+                       stored_supportedGametypes |= MAPINFO_TYPE_CTS.m_flags;
+               }
+               else if(t == "map")
+               {
+                       if(strtolower(s) == strtolower(pFilename))
+                               dosave = true; // yay, found our map!
+               }
+               else if(t == "quote")
+                       stored_Map_description = s;
+               // TODO: fraglimit
+       }
+
+       // if the map wasn't found in the .arena, fall back to generated .mapinfo
+       return false;
+}
+
+#if defined(CSQC) || defined(MENUQC)
+string(string filename) whichpack = #503;
+#endif
+string _MapInfo_CheckArenaFile(string pFilename, string pMapname)
+{
+       // returns the file name if valid, otherwise returns ""
+       // a string is returned to optimise the use cases where a filename is also returned
+       int fh = fopen(pFilename, FILE_READ);
+       if(fh < 0)
+               return "";
+       for(string s; (s = fgets(fh)); )
+       {
+               s = strreplace("\t", "", s);
+               while(substring(s, 0, 1) == " ")
+                       s = substring(s, 1, -1);
+               if(substring(s, 0, 2) == "//")
+                       continue;
+               if(s == "")
+                       continue;
+               int offset = strstrofs(s, "map", 0);
+               if(offset >= 0)
+               {
+                       if(strstrofs(strtolower(s), strcat("\"", strtolower(pMapname), "\""), offset) >= 0) // quake 3 is case insensitive
+                       {
+                               fclose(fh);
+                               return pFilename; // FOUND IT!
+                       }
+               }
+       }
+       fclose(fh);
+       return ""; // file did not contain a "map" field matching our map name
+}
+
+string _MapInfo_FindArenaFile(string pFilename, string extension)
+{
+       string fallback = strcat("scripts/", pFilename, extension);
+       if(!checkextension("DP_QC_FS_SEARCH_PACKFILE"))
+               return _MapInfo_CheckArenaFile(fallback, pFilename);
+       string base_pack = whichpack(strcat("maps/", pFilename, ".bsp"));
+       if(base_pack == "") // this map isn't packaged!
+               return _MapInfo_CheckArenaFile(fallback, pFilename);
+
+       int glob = search_packfile_begin(strcat("scripts/*", extension), true, true, base_pack);
+       if(glob < 0)
+               return _MapInfo_CheckArenaFile(fallback, pFilename);
+       int n = search_getsize(glob);
+       for(int j = 0; j < n; ++j)
+       {
+               string file = search_getfilename(glob, j);
+               if(_MapInfo_CheckArenaFile(file, pFilename) != "")
+               {
+                       search_end(glob);
+                       return file;
+               }
+       }
+
+       search_end(glob);
+       return ""; // if we get here, a valid .arena file could not be found
+}
+
 // load info about a map by name into the MapInfo_Map_* globals
 float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gametype pGametypeToSet)
 {
@@ -762,7 +1002,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
        string s, t;
        float fh;
        int f, i;
-       float r, n, p;
+       float r, n;
        string acl;
 
        acl = MAPINFO_SETTEMP_ACL_USER;
@@ -786,6 +1026,30 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
        fh = fopen(fn, FILE_READ);
        if(fh < 0)
        {
+               if(autocvar_g_mapinfo_q3compat) // use arena data instead of generating a mapinfo file
+               {
+                       bool isdefi = false;
+                       if(autocvar_g_mapinfo_q3compat == 1) // only parse .arena files in mode 1
+                       {
+                               fn = _MapInfo_FindArenaFile(pFilename, ".arena");
+                               if(fn != "")
+                                       fh = fopen(fn, FILE_READ);
+                       }
+                       if(fh < 0 || autocvar_g_mapinfo_q3compat == 2)
+                       {
+                               isdefi = true;
+                               fn = _MapInfo_FindArenaFile(pFilename, ".defi");
+                               if(fn != "")
+                                       fh = fopen(fn, FILE_READ);
+                       }
+                       if(fh >= 0)
+                       {
+                               _MapInfo_Map_Reset();
+                               if(_MapInfo_ParseArena(fn, fh, pFilename, pGametypeToSet, isdefi, false))
+                                       goto mapinfo_handled; // skip generation
+                       }
+               }
+
                fn = strcat("maps/autogenerated/", pFilename, ".mapinfo");
                fh = fopen(fn, FILE_READ);
                if(fh < 0)
@@ -802,12 +1066,8 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                        fputs(fh, strcat("author ", MapInfo_Map_author, "\n"));
                        if(_MapInfo_Map_worldspawn_music != "")
                        {
-                               if(
-                                       substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".wav"
-                                       ||
-                                       substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".ogg"
-                               )
-                                       fputs(fh, strcat("cdtrack ", substring(_MapInfo_Map_worldspawn_music, 0, strlen(_MapInfo_Map_worldspawn_music) - 4), "\n"));
+                               if(strcasecmp(substring(_MapInfo_Map_worldspawn_music, -4, 4), ".wav") == 0 || strcasecmp(substring(_MapInfo_Map_worldspawn_music, -4, 4), ".ogg") == 0)
+                                       fputs(fh, strcat("cdtrack ", substring(_MapInfo_Map_worldspawn_music, 0, -4), "\n"));
                                else
                                        fputs(fh, strcat("cdtrack ", _MapInfo_Map_worldspawn_music, "\n"));
                        }
@@ -876,7 +1136,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                if(substring(s, 0, 1) == "_")  // q3map style
                        continue;
 
-               p = strstrofs(s, "//", 0);
+               float p = strstrofs(s, "//", 0);
                if(p >= 0)
                        s = substring(s, 0, p);
 
@@ -922,7 +1182,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                else if(t == "type")
                {
                        t = car(s); s = cdr(s);
-                       Gametype f = MapInfo_Type_FromString(t, true);
+                       Gametype f = MapInfo_Type_FromString(t, true, false);
                        //if(WARN_COND)
                                //LOG_WARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.");
                        if(f)
@@ -933,7 +1193,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                else if(t == "gametype")
                {
                        t = car(s); s = cdr(s);
-                       Gametype f = MapInfo_Type_FromString(t, true);
+                       Gametype f = MapInfo_Type_FromString(t, true, false);
                        if(f)
                                _MapInfo_Map_ApplyGametypeEx (s, pGametypeToSet, f);
                        else if(WARN_COND)
@@ -984,7 +1244,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                        t = car(s); s = cdr(s);
                        bool all = t == "all";
                        Gametype f = NULL;
-                       if(all || (f = MapInfo_Type_FromString(t, true)))
+                       if(all || (f = MapInfo_Type_FromString(t, true, false)))
                        {
                                if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags)
                                {
@@ -1001,7 +1261,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                        t = car(s); s = cdr(s);
                        bool all = t == "all";
                        Gametype f = NULL;
-                       if(all || (f = MapInfo_Type_FromString(t, true)))
+                       if(all || (f = MapInfo_Type_FromString(t, true, false)))
                        {
                                if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags)
                                {
@@ -1048,6 +1308,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet
                else if(WARN_COND)
                        LOG_WARN("Map ", pFilename, " provides unknown info item ", t, ", ignored");
        }
+       LABEL(mapinfo_handled)
        fclose(fh);
 
        if(MapInfo_Map_title == "<TITLE>")
@@ -1149,7 +1410,7 @@ int MapInfo_CurrentFeatures()
 
 Gametype MapInfo_CurrentGametype()
 {
-       Gametype prev = MapInfo_Type_FromString(cvar_string("gamecfg"), false);
+       Gametype prev = MapInfo_Type_FromString(cvar_string("gamecfg"), false, false);
        FOREACH(Gametypes, cvar(it.netname) && it != prev, return it);
        return prev ? prev : MAPINFO_TYPE_DEATHMATCH;
 }
index a7bf6ce0aab77fdcb4718c0f084998c30da9f517..f7ca99a21d809dbb6daa72f451df248913879456 100644 (file)
@@ -186,7 +186,7 @@ string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
 // gets a gametype from a string
 string _MapInfo_GetDefaultEx(Gametype t);
 float _MapInfo_GetTeamPlayBool(Gametype t);
-Gametype MapInfo_Type_FromString(string t, bool dowarn);
+Gametype MapInfo_Type_FromString(string t, bool dowarn, bool is_q3compat);
 string MapInfo_Type_Description(Gametype t);
 string MapInfo_Type_ToString(Gametype t);
 string MapInfo_Type_ToText(Gametype t);
@@ -200,6 +200,10 @@ void MapInfo_Cache_Destroy(); // disable caching
 void MapInfo_Cache_Create(); // enable caching
 void MapInfo_Cache_Invalidate(); // delete cache if any, but keep enabled
 
+bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gametype pGametypeToSet, bool isdefi, bool isgenerator);
+
+string _MapInfo_FindArenaFile(string pFilename, string extension);
+
 void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse);
 
 void MapInfo_ClearTemps(); // call this when done with mapinfo for this frame
index 876fed7a7306cab48686a8adef6c1e18caf94e0c..2cc3c58ba4f32479b8213528b3fd4aea6adfb655 100644 (file)
@@ -28,7 +28,7 @@ void target_changelevel_use(entity this, entity actor, entity trigger)
        }
 
        if(this.gametype != "")
-               MapInfo_SwitchGameType(MapInfo_Type_FromString(this.gametype, false));
+               MapInfo_SwitchGameType(MapInfo_Type_FromString(this.gametype, false, false));
 
        if (this.chmap == "")
        {
index e29939909734c14f241d15bb1e449552b492f0c9..c9bfebabd415c4414eea269638f686fbd4a94f6b 100644 (file)
@@ -173,6 +173,7 @@ noref string __fullspawndata;
                FIELD_SCALAR(fld, monster_name) \
                FIELD_SCALAR(fld, movetype) \
                FIELD_SCALAR(fld, move_movetype) \
+               FIELD_SCALAR(fld, music) \
                FIELD_SCALAR(fld, netname) \
                FIELD_SCALAR(fld, nextthink) \
                FIELD_SCALAR(fld, noalign) \
index 0b29572bee644865b70ebc3ddc6815bdc3806fa0..2bfb955c575017ca614e100e40ff82d79b0eb201 100644 (file)
@@ -83,6 +83,17 @@ vector draw_PictureSize(string pic)
        return drawgetimagesize(pic);
 }
 
+bool draw_PictureExists(string pic)
+{
+       pic = draw_UseSkinFor(pic);
+       if (fexists(strcat(pic, ".tga"))) return true;
+       if (fexists(strcat(pic, ".png"))) return true;
+       if (fexists(strcat(pic, ".jpg"))) return true;
+       if (fexists(strcat(pic, ".pcx"))) return true;
+
+       return false;
+}
+
 void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha)
 {
        drawfill(boxToGlobal(theOrigin, draw_shift, draw_scale), boxToGlobalSize(theSize, draw_scale), theColor, theAlpha * draw_alpha, 0);
index 69178cb03eedb522c29a0597b22c9f09303133cb..611281913d8d8ed209123a8ae4ec4813f8d5cf81 100644 (file)
@@ -21,6 +21,7 @@ void draw_VertButtonPicture(vector theOrigin, string pic, vector theSize, vector
 void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector theColor, float theAlpha, vector theBorderSize);
 void draw_Picture(vector origin, string pic, vector size, vector color, float alpha);
 vector draw_PictureSize(string pic);
+bool draw_PictureExists(string pic);
 void draw_Fill(vector theOrigin, vector theSize, vector theColor, float theAlpha);
 void draw_Text(vector origin, string text, vector size, vector color, float alpha, float allowColorCodes);
 void draw_CenterText(vector origin, string text, vector size, vector color, float alpha, float allowColorCodes);
@@ -40,3 +41,4 @@ vector globalToBoxSize(vector v, vector scale);
 
 float draw_TextWidth_WithColors(string s, vector size);
 float draw_TextWidth_WithoutColors(string s, vector size);
+
index c9db70116f0f171729d6e3115aa20f06233bc2de..d32bd0f8401eee0314deff72edef2468ae4e3f27 100644 (file)
@@ -17,12 +17,14 @@ void XonoticMapInfoDialog_loadMapInfo(entity me, int i, entity mlb)
        strcpy(me.currentMapAuthor, strdecolorize(MapInfo_Map_author));
        strcpy(me.currentMapDescription, MapInfo_Map_description);
        strcpy(me.currentMapPreviewImage, strcat("/maps/", MapInfo_Map_bspname));
+       if(!draw_PictureExists(me.currentMapPreviewImage)) // Quake 3 compatibility
+               strcpy(me.currentMapPreviewImage, strcat("/levelshots/", MapInfo_Map_bspname));
 
        me.frame.setText(me.frame, me.currentMapBSPName);
        me.titleLabel.setText(me.titleLabel, me.currentMapTitle);
        me.authorLabel.setText(me.authorLabel, me.currentMapAuthor);
        me.descriptionLabel.setText(me.descriptionLabel, me.currentMapDescription);
-       if(draw_PictureSize(me.currentMapPreviewImage) == '0 0 0')
+       if(!draw_PictureExists(me.currentMapPreviewImage))
                me.previewImage.src = "nopreview_map";
        else
                me.previewImage.src = me.currentMapPreviewImage;
index 7ff09655a39fdbdc6ca2ba03b8b4e98f12c48f04..59fe45561850fbf34ce2103a00b50fa5f7e39d6d 100644 (file)
@@ -114,7 +114,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i)
        if(s != "data")
                modname = sprintf("%s (%s)", modname, s);
 
-       Gametype j = MapInfo_Type_FromString(typestr, false); // try and get the real name of the game type
+       Gametype j = MapInfo_Type_FromString(typestr, false, false); // try and get the real name of the game type
        if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it
 
        me.currentServerType = strzone(typestr);
index 72421150852367bb1379ccc6427cbef05cec4a37..1fa065874554ce666fa883884d2347359d076bf0 100644 (file)
@@ -157,7 +157,12 @@ void XonoticMapList_drawListBoxItem(entity me, int i, vector absSize, bool isSel
        }
 
        if(draw_PictureSize(strcat("/maps/", MapInfo_Map_bspname)) == '0 0 0')
-               draw_Picture(me.columnPreviewOrigin * eX, "nopreview_map", me.columnPreviewSize * eX + eY, '1 1 1', theAlpha);
+       {
+               if(!draw_PictureExists(strcat("/levelshots/", MapInfo_Map_bspname)))
+                       draw_Picture(me.columnPreviewOrigin * eX, "nopreview_map", me.columnPreviewSize * eX + eY, '1 1 1', theAlpha);
+               else
+                       draw_Picture(me.columnPreviewOrigin * eX, strcat("/levelshots/", MapInfo_Map_bspname), me.columnPreviewSize * eX + eY, '1 1 1', theAlpha);
+       }
        else
                draw_Picture(me.columnPreviewOrigin * eX, strcat("/maps/", MapInfo_Map_bspname), me.columnPreviewSize * eX + eY, '1 1 1', theAlpha);
 
index 6bd853d1b4af51d053ff9048f5712d184ca60986..bdb704b985d5beba3e3f707f71c4615a480cc08a 100644 (file)
@@ -33,7 +33,7 @@ float Campaign_Invalid()
        string thismapname, wantedmapname;
        thismapname = GetMapname();
        wantedmapname = campaign_gametype[0];
-       if(MapInfo_CurrentGametype() != MapInfo_Type_FromString(wantedmapname, false))
+       if(MapInfo_CurrentGametype() != MapInfo_Type_FromString(wantedmapname, false, false))
                return CampaignBailout("wrong game type!");
        wantedmapname = campaign_mapname[0];
        if(wantedmapname != thismapname)
@@ -72,7 +72,7 @@ void CampaignPreInit()
 
        if(autocvar_sv_cheats)
        {
-               MapInfo_SwitchGameType(MapInfo_Type_FromString(campaign_gametype[0], false));
+               MapInfo_SwitchGameType(MapInfo_Type_FromString(campaign_gametype[0], false, false));
                CampaignFile_Unload();
                CampaignBailout("JOLLY CHEATS AHAHAHAHAHAHAH))");
                return;
@@ -101,7 +101,7 @@ void CampaignPreInit()
        cvar_settemp("skill", ftos(baseskill));
        cvar_settemp("bot_number", ftos(campaign_bots[0]));
 
-       MapInfo_SwitchGameType(MapInfo_Type_FromString(campaign_gametype[0], false));
+       MapInfo_SwitchGameType(MapInfo_Type_FromString(campaign_gametype[0], false, false));
 
        if(Campaign_Invalid())
                return;
index b35f4fb53ae4e6ad52687a964a8782a58889d701..799c3426f61f9c6ac32e9f91170230b61491f650 100644 (file)
@@ -729,7 +729,7 @@ void GameCommand_gametype(int request, int argc)
                        if (argv(1) != "")
                        {
                                string s = argv(1);
-                               Gametype t = MapInfo_Type_FromString(s, false), tsave = MapInfo_CurrentGametype();
+                               Gametype t = MapInfo_Type_FromString(s, false, false), tsave = MapInfo_CurrentGametype();
 
                                if (t)
                                {
index 9242a5b6f4d057aa99e95f356cf80098eb2f6a12..c73f97da961643c4cbe3b791e93dea3b880e73f1 100644 (file)
@@ -48,10 +48,10 @@ entity mapvote_ent;
  */
 Gametype GameTypeVote_Type_FromString(string type_name)
 {
-       Gametype type = MapInfo_Type_FromString(type_name, false);
+       Gametype type = MapInfo_Type_FromString(type_name, false, false);
        if (type == NULL)
                type = MapInfo_Type_FromString(cvar_string(
-                       strcat("sv_vote_gametype_",type_name,"_type")), false);
+                       strcat("sv_vote_gametype_",type_name,"_type")), false, false);
        return type;
 }
 
@@ -59,11 +59,11 @@ int GameTypeVote_AvailabilityStatus(string type_name)
 {
        int flag = GTV_FORBIDDEN;
 
-       Gametype type = MapInfo_Type_FromString(type_name, false);
+       Gametype type = MapInfo_Type_FromString(type_name, false, false);
        if ( type == NULL )
        {
                type = MapInfo_Type_FromString(cvar_string(
-                       strcat("sv_vote_gametype_",type_name,"_type")), false);
+                       strcat("sv_vote_gametype_",type_name,"_type")), false, false);
                flag |= GTV_CUSTOM;
        }
 
index bd713ecd09cea6562c5f41e6eef7b06455eb7641..5307f56d3d246f0b019845a1b929b4604337f76c 100644 (file)
@@ -303,6 +303,7 @@ void cvar_changes_init()
                BADCVAR("nextmap");
                BADCVAR("teamplay");
                BADCVAR("timelimit");
+               BADCVAR("g_mapinfo_q3compat");
                BADCVAR("g_mapinfo_settemp_acl");
                BADCVAR("g_mapinfo_ignore_warnings");
                BADCVAR("g_maplist_ignore_sizes");
@@ -858,8 +859,22 @@ spawnfunc(worldspawn)
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1);
 
-       q3compat = BITSET(q3compat, Q3COMPAT_ARENA, fexists(strcat("scripts/", mapname, ".arena")));
-       q3compat = BITSET(q3compat, Q3COMPAT_DEFI, fexists(strcat("scripts/", mapname, ".defi")));
+       q3compat = BITSET(q3compat, Q3COMPAT_ARENA, _MapInfo_FindArenaFile(mapname, ".arena") != "");
+       q3compat = BITSET(q3compat, Q3COMPAT_DEFI, _MapInfo_FindArenaFile(mapname, ".defi") != "");
+
+       // quake 3 music support
+       if(world.music || world.noise)
+       {
+               // prefer .music over .noise
+               string chosen_music;
+               if(world.music)
+                       chosen_music = world.music;
+               else
+                       chosen_music = world.noise;
+
+               string newstuff = strcat(clientstuff, "cd loop \"", chosen_music, "\"\n");
+               strcpy(clientstuff, newstuff);
+       }
 
        if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
        {
index e74ab2dc7589733b4ca9e4034ab587c49daa78d1..a773cd3b1072dd1a7bb4e16622ef977154cfce13 100644 (file)
@@ -150,6 +150,10 @@ float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, f
 void CheckRules_World();
 float RedirectionThink();
 
+// quake 3 music compatibility
+.string music;
+.string noise;
+
 void readplayerstartcvars();
 
 void readlevelcvars();
index dc9f1f080a74ddfa82e0dadc2208ab6d82cd7eea..4dac576ec5ba2bc6fe8cea88f402b5529d1bc651 100644 (file)
@@ -163,6 +163,9 @@ set debug_deglobalization_clear 0 "make the new wrappers set globals to NaN afte
 // disabling until it's complete
 set prvm_garbagecollection_enable 0
 
+set g_mapinfo_arena_compat 1 "allow mapinfo data to be pulled directly from .arena and .defi files if they exist, rather than generating .mapinfo files for them"
+set g_mapinfo_arena_generate 0 "allow mapinfo data to be pulled from .arena and .defi files during generation"
+
 // load console command aliases and settings
 exec commands.cfg
 
index 334f749a2552baf0ab69165c106bbbb2aad469c8..9140e6f802d82bca764a140e8542d5a6cc191491 100644 (file)
@@ -504,7 +504,8 @@ sv_gameplayfix_consistentplayerprethink 1
 sv_gameplayfix_gravityunaffectedbyticrate 1
 sv_gameplayfix_nogravityonground 1
 
-set sv_q3compat_changehitbox 0 "use Q3 player hitbox dimensions and camera height on Q3 maps (maps with an entry in a .arena or .defi file)
+set sv_q3compat_changehitbox 0 "use Q3 player hitbox dimensions and camera height on Q3 maps (maps with an entry in a .arena or .defi file)"
+set g_mapinfo_q3compat 1 "0: generate .mapinfo if none exists, ignoring .arena and .defi files. 1: read .arena and .defi files, don't generate .mapinfo. 2: generate .mapinfo if none exists using data from .arena and .defi files instead of reading them"
 
 set g_movement_highspeed 1 "multiplier scale for movement speed (applies to sv_maxspeed and sv_maxairspeed, also applies to air acceleration when g_movement_highspeed_q3_compat is set to 0)"
 set g_movement_highspeed_q3_compat 0 "apply speed modifiers to air movement in a more Q3-compatible way (only apply speed buffs and g_movement_highspeed to max air speed, not to acceleration)"